12/28/2021

[Bug] Google Cloud Run decodes url search part before reaching your app

Today I discovered a (insane 🤯) bug in Google Cloud Run.

I was trying to understand WHY 😵‍💫 Image-Charts works on Google Kubernetes Engine (GKE) but the same container does not work on Cloud-Run.

To be precise, why the signed URL (HMAC) below works on GKE and not on Cloud-Run

https://image-charts.com/chart?chbr=8&chd=t%3A10%2C15%2C25%2C30%2C40%2C80&chf=b0%2Clg%2C90%2C05B142%2C1%2C0CE858%2C0.2&chl=%7C%7C%7C%7C%2033%25%20%21%7Cx2%20&chma=0%2C0%2C10%2C10&chs=700x450&cht=bvs&chtt=Revenue%20per%20month&chxl=0%3A%7CJan%7CFev%7CMar%7CAvr%7CMay&chxs=1N%2AcUSD0sz%2A%2C000000%2C14&chxt=x%2Cy&icac=fgribreau&ichm=d92a75886c0657013da32eda4d82b8db3af39d355b3419caebd5952bc827990d

Note that HMAC signature (ichm query parameter) is computed like this:

ichm=sign(url.search, shared_secret)

My main intuition was that cloud-run had some internal reverse proxies that changes part of the URL on the fly.

If that's trye, let's find what encoded characters Cloud-Run would automatically decode in the URL query part before reaching your app:

new Array(155).join('-').split('-').map((x, i) => '%'+i.toString(16).toUpperCase()).join('-_-')

// output:
// %0-_-%1-_-%2-_-%3-_-%4-_-%5-_-%6-_-%7-_-%8-_-%9-_-%A-_-%B-_-%C-_-%D-_-%E-_-%F-_-%10-_-%11-_-%12-_-%13-_-%14-_-%15-_-%16-_-%17-_-%18-_-%19-_-%1A-_-%1B-_-%1C-_-%1D-_-%1E-_-%1F-_-%20-_-%21-_-%22-_-%23-_-%24-_-%25-_-%26-_-%27-_-%28-_-%29-_-%2A-_-%2B-_-%2C-_-%2D-_-%2E-_-%2F-_-%30-_-%31-_-%32-_-%33-_-%34-_-%35-_-%36-_-%37-_-%38-_-%39-_-%3A-_-%3B-_-%3C-_-%3D-_-%3E-_-%3F-_-%40-_-%41-_-%42-_-%43-_-%44-_-%45-_-%46-_-%47-_-%48-_-%49-_-%4A-_-%4B-_-%4C-_-%4D-_-%4E-_-%4F-_-%50-_-%51-_-%52-_-%53-_-%54-_-%55-_-%56-_-%57-_-%58-_-%59-_-%5A-_-%5B-_-%5C-_-%5D-_-%5E-_-%5F-_-%60-_-%61-_-%62-_-%63-_-%64-_-%65-_-%66-_-%67-_-%68-_-%69-_-%6A-_-%6B-_-%6C-_-%6D-_-%6E-_-%6F-_-%70-_-%71-_-%72-_-%73-_-%74-_-%75-_-%76-_-%77-_-%78-_-%79-_-%7A-_-%7B-_-%7C-_-%7D-_-%7E-_-%7F-_-%80-_-%81-_-%82-_-%83-_-%84-_-%85-_-%86-_-%87-_-%88-_-%89-_-%8A-_-%8B-_-%8C-_-%8D-_-%8E-_-%8F-_-%90-_-%91-_-%92-_-%93-_-%94-_-%95-_-%96-_-%97-_-%98-_-%99-_-%9A

To run this experiment I've set a special route on Image-Charts that outputs what Image-Charts http server got as part of url.search.

On Google Kubernetes Engine (GKE), Image Charts API got an unmodified query string:

%0-_-%1-_-%2-_-%3-_-%4-_-%5-_-%6-_-%7-_-%8-_-%9-_-%A-_-%B-_-%C-_-%D-_-%E-_-%F-_-%10-_-%11-_-%12-_-
%13-_-%14-_-%15-_-%16-_-%17-_-%18-_-%19-_-%1A-_-%1B-_-%1C-_-%1D-_-%1E-_-%1F-_-%20-_-%21-_-%22-_-
%23-_-%24-_-%25-_-%26-_-%27-_-%28-_-%29-_-%2A-_-%2B-_-%2C-_-%2D-_-%2E-_-%2F-_-%30-_-%31-_-%32-_-
%33-_-%34-_-%35-_-%36-_-%37-_-%38-_-%39-_-%3A-_-%3B-_-%3C-_-%3D-_-%3E-_-%3F-_-%40-_-%41-_-%42-_-
%43-_-%44-_-%45-_-%46-_-%47-_-%48-_-%49-_-%4A-_-%4B-_-%4C-_-%4D-_-%4E-_-%4F-_-%50-_-%51-_-%52-_-
%53-_-%54-_-%55-_-%56-_-%57-_-%58-_-%59-_-%5A-_-%5B-_-%5C-_-%5D-_-%5E-_-%5F-_-%60-_-%61-_-%62-_-
%63-_-%64-_-%65-_-%66-_-%67-_-%68-_-%69-_-%6A-_-%6B-_-%6C-_-%6D-_-%6E-_-%6F-_-%70-_-%71-_-%72-_-
%73-_-%74-_-%75-_-%76-_-%77-_-%78-_-%79-_-%7A-_-%7B-_-%7C-_-%7D-_-%7E-_-%7F-_-%80-_-%81-_-%82-_-
%83-_-%84-_-%85-_-%86-_-%87-_-%88-_-%89-_-%8A-_-%8B-_-%8C-_-%8D-_-%8E-_-%8F-_-%90-_-%91-_-%92-_-
%93-_-%94-_-%95-_-%96-_-%97-_-%98-_-%99-_-%9A

On Google Cloud Run however, things are different:

%0-_-%1-_-%2-_-%3-_-%4-_-%5-_-%6-_-%7-_-%8-_-%9-_-%A-_-%B-_-%C-_-%D-_-%E-_-%F-_-%10-_-%11-_-%12-_-
%13-_-%14-_-%15-_-%16-_-%17-_-%18-_-%19-_-%1A-_-%1B-_-%1C-_-%1D-_-%1E-_-%1F-_-%20-_-!-_-%22-_-
%23-_-%24-_-%25-_-%26-_-%27-_-(-_-)-_-*-_-%2B-_-%2C-_---_-.-_-%2F-_-0-_-1-_-2-_-
3-_-4-_-5-_-6-_-7-_-8-_-9-_-%3A-_-%3B-_-%3C-_-%3D-_-%3E-_-%3F-_-%40-_-A-_-B-_-
C-_-D-_-E-_-F-_-G-_-H-_-I-_-J-_-K-_-L-_-M-_-N-_-O-_-P-_-Q-_-R-_-
S-_-T-_-U-_-V-_-W-_-X-_-Y-_-Z-_-%5B-_-%5C-_-%5D-_-%5E-_-_-_-%60-_-a-_-b-_-
c-_-d-_-e-_-f-_-g-_-h-_-i-_-j-_-k-_-l-_-m-_-n-_-o-_-p-_-q-_-r-_-
s-_-t-_-u-_-v-_-w-_-x-_-y-_-z-_-%7B-_-%7C-_-%7D-_-~-_-%7F-_-%80-_-%81-_-%82-_-
%83-_-%84-_-%85-_-%86-_-%87-_-%88-_-%89-_-%8A-_-%8B-_-%8C-_-%8D-_-%8E-_-%8F-_-%90-_-%91-_-%92-_-
%93-_-%94-_-%95-_-%96-_-%97-_-%98-_-%99-_-%9A

Yep. Characters like ")", "(", "a", "~" were converted on the fly by some Cloud Run internal proxy.

Now lets filter this for better readability:

const search_raw = new Array(155).join('-').split('-').map((x, i) => '%'+i.toString(16).toUpperCase());
const search_processed_by_cloud_run = `%0-_-%1-_-%2-_-%3-_-%4-_-%5-_-%6-_-%7-_-%8-_-%9-_-%A-_-%B-_-%C-_-%D-_-%E-_-%F-_-%10-_-%11-_-%12-_-%13-_-%14-_-%15-_-%16-_-%17-_-%18-_-%19-_-%1A-_-%1B-_-%1C-_-%1D-_-%1E-_-%1F-_-%20-_-!-_-%22-_-%23-_-%24-_-%25-_-%26-_-%27-_-(-_-)-_-*-_-%2B-_-%2C-_---_-.-_-%2F-_-0-_-1-_-2-_-3-_-4-_-5-_-6-_-7-_-8-_-9-_-%3A-_-%3B-_-%3C-_-%3D-_-%3E-_-%3F-_-%40-_-A-_-B-_-C-_-D-_-E-_-F-_-G-_-H-_-I-_-J-_-K-_-L-_-M-_-N-_-O-_-P-_-Q-_-R-_-S-_-T-_-U-_-V-_-W-_-X-_-Y-_-Z-_-%5B-_-%5C-_-%5D-_-%5E-_-_-_-%60-_-a-_-b-_-c-_-d-_-e-_-f-_-g-_-h-_-i-_-j-_-k-_-l-_-m-_-n-_-o-_-p-_-q-_-r-_-s-_-t-_-u-_-v-_-w-_-x-_-y-_-z-_-%7B-_-%7C-_-%7D-_-~-_-%7F-_-%80-_-%81-_-%82-_-%83-_-%84-_-%85-_-%86-_-%87-_-%88-_-%89-_-%8A-_-%8B-_-%8C-_-%8D-_-%8E-_-%8F-_-%90-_-%91-_-%92-_-%93-_-%94-_-%95-_-%96-_-%97-_-%98-_-%99-_-%9A`.split('-_-');

search_raw.reduce((m, encoded, i) => encoded === search_processed_by_cloud_run[i] ? m : m.concat([encoded, search_processed_by_cloud_run[i]].join(' => ')), []).join('\n')
    
// output
%21 => !
%28 => (
%29 => )
%2A => *
%2D => -
%2E => .
%30 => 0
%31 => 1
%32 => 2
%33 => 3
%34 => 4
%35 => 5
%36 => 6
%37 => 7
%38 => 8
%39 => 9
%41 => A
%42 => B
%43 => C
%44 => D
%45 => E
%46 => F
%47 => G
%48 => H
%49 => I
%4A => J
%4B => K
%4C => L
%4D => M
%4E => N
%4F => O
%50 => P
%51 => Q
%52 => R
%53 => S
%54 => T
%55 => U
%56 => V
%57 => W
%58 => X
%59 => Y
%5A => Z
%5F => _
%61 => a
%62 => b
%63 => c
%64 => d
%65 => e
%66 => f
%67 => g
%68 => h
%69 => i
%6A => j
%6B => k
%6C => l
%6D => m
%6E => n
%6F => o
%70 => p
%71 => q
%72 => r
%73 => s
%74 => t
%75 => u
%76 => v
%77 => w
%78 => x
%79 => y
%7A => z
%7E => ~

I reported this bug to Steren (Product Manager at Google Cloud Run) and hopefully it will be fixed soon :)

12/15/2021

[fr] oxmoto.fr — stratégie e-commerce 1 an plus tard

Cela fait maintenant 1 an (2020) que j'ai rejoins en tant qu'associé Oxmoto afin d'y développer la partie e-commerce de Oxmoto.fr grâce à un suivi hebdomadaire. 

Fin 2021 nous avons maintenant franchit une étape importante car le CA réalisé chaque semaine correspond au CA annuel de 2020. 

C'est le résultat d'efforts continus de la part de toute l'équipe, d'une approche aggressive #lean et d'une excellence d'exécution à tous les niveaux. 

2022 s'annonce exceptionnel !
6/16/2021

[Fr] Une sombre histoire de vaccin, d'erreur 500 de FSM et de routing

L’infirmière a une erreur 500 sur AmeliPro lorsqu’elle souhaitait valider ma première dose de vaccin 🥲

Bien entendu — comme tout utilisateur — elle fait donc un retour arrière mais l’application ne respectant pas les principes du web : pas d’état porté par l’url -> 🔥💥☠️🥲

Qu’a-t-elle donc fait ?
Recommencer le cheminement à 0 🥲

Chez Cloud-IAM, Hook0 et feu Redsmin et Bringr nous respect(i)ons le principe de « les interfaces graphiques en tant que machines d'états finis » qui map chaque état sur un routing associé.

Pour faire simple un état (~ un écran) = une route côté front.

La *majorité* des états sont donc accessibles via une URL associée, permettant de nombreux usages internes et externes (partage de lien, bookmarks, retour arrière, debugging, compréhension globale des parcours utilisateurs, documentation à jour, automatisation etc…)

Envie d'en savoir plus ? J’en parle en détail dans mon livre !

2/17/2021

How to setup your own tmate server with docker

tmate is an awesome tool I use to do work/debug session on someone else terminal. Learn more about tmate here. Sadly their default server is not available anymore, at the time of writing this article you have to setup your own tmate server, I could not find a lot of documentation on internet so here we are!

# ssh into your server (e.g. 51.158.172.10)
# install docker
mkdir tmate && cd tmate

# this command will download the create_keys script and create a "keys" folder in the directory
curl -s -q https://raw.githubusercontent.com/tmate-io/tmate-ssh-server/master/create_keys.sh | bash

# don't forget to setup tmate client on the machine you want and configure ~/.tmate.conf with the information outputed by the previous command

# now let's start the server (don't forget to change "sub.my-domain-name.com") with the domain name pointing to your server
# I choose 2223 but any other port will do

docker run -d --name="tmate-server" \
  --cap-add SYS_ADMIN \
  -v  $(pwd)/keys:/keys \
  -e SSH_KEYS_PATH=/keys \
  -p 2223:2223 \
  -e SSH_PORT_LISTEN=2223 \
  -e SSH_HOSTNAME=sub.my-domain-name.com \
  -e USE_PROXY_PROTOCOL=0 \
  tmate/tmate-ssh-server:prod

Now back on your client machines (e.g. personal laptops, or your friend laptop):

# install tmate client (check https://tmate.io/ for instructions)
nano ~/.tmate.conf

# paste what was printed from create_keys.sh

set -g tmate-server-host "IP_OR_DOMAIN_OF_YOUR_SERVER"
set -g tmate-server-port 2223
set -g tmate-server-rsa-fingerprint SHA256:xxxxxxxxxxxxxxxxxx
set -g tmate-server-ed25519-fingerprint SHA256:xxxxxxxxxxxxxxxxxxxxxx

You now are good to go!

1/17/2021

[Fr] La signature numérique de Sylae ? Bonjour Sisyphe, bienvenue en enfer

L'histoire suivante se passe sur le site Sylae, un site du service-public.

Quelques "MINUTES" pour afficher un formulaire de signature numérique ?!.

C'est forcément parce que vous êtes en train de me générer une clé avec une complexité de tarée right ? RIGHT ?

Moi dans ces cas là, j'ai juste pas la patience (après 10 secondes d'attente), je check le code-source.

Et comme c'est un service publique, il y a 80% de chance que ça soit un grand moment (Atos, CapGemini, Sopra, ... FTW).

(issTimeout=300)

"Allez, on va dire qu'au bout de 5 minutes, si le formulaire n'est pas chargé, c'est qu'il y a un problème".

NON MAIS LES GARS si vous n'étiez pas les services publics, la moitié du web aurait quitté cette page après 5 secondes !

Le script JS génère du mixed content, les mecs doutent de rien. OKLM.

Mais savez pourquoi ils affichent après 5 MINUTES le message: "Nous vous invitons à vérifier la configuration de votre poste de travail avec les pré-requis" ????????

En fait vous DEVIEZ savoir préalablement qu'il fallait lire un PDF pour ajouter des EXCEPTIONS à l'exécution de l'applet Java en HTTP (sisisisisi). 

Les mixed content en fait c'était pas un bug, mais une feature. 

Cher utilisateur, corrige le problème CHEZ TOI (pov' naz).

Fun fact: il n'y a pas de lien depuis le message pour vous guider vers "la configuration" optimale du "poste de travail" en question (ça serait trop vous aider bande de fainéant)

Une recherche google m'emmène le site d'une communauté de commune qui héberge le PDF DE Sylae.

En conclusion : 

  • on te fait dé-facto poireauter 5 minutes
  • on t'affiche un message pour te dire que t'es qu'une merde (je grossis à peine le trait) 
  • ... qu'il te faut vérifier la "configuration" de "ton poste de travail"
  • ... qui consiste à mettre en place un bypass sécurité

Quand on connait les deux grosses sociétés qui sont derrière le développement et l'hébergement de la majorité des applications des services publiques en France... 
... et le niveau des développements là bas 
... et leurs coût

Merci. Changez rien.

[Bonus] Si vous demandez l'impression manuscrite avec signature, on vous affiche ce message. Les mecs n'ont peur de rien.

[Bonus 2] Je vous explique la blague ou bien ?

[Bonus 3] "deployJava.js" quand même Oracle s'y met. La factorisation ? Pour quoi faire ?

Après 1h30 de bataille. 

  • Brave (good point) ne permettant même pas de désactiver le mixed-content. Contrairement à Chrome/Firefox.
  • Chrome/Firefox/Brave ne supportant plus Java. 
Je me décide à faire l'impensable :

... sauf que c'était trop demander à Microsoft:<+p>

C'est partiiiiiiiiiiiiiiiii... 

[3h plus tard. 3. Heures.] Et 1 VM et le vieux windows de ma femme. Toujours pas.

MAIS NOOOOOOON ! Redirection http => https => homepage

Les plus assidus remarquerons que la redirection depuis https va vers une adresse en ... http
A votre avis, le système Sylaé a: 

  1. enregistré mon mot de passe auto-généré MAIS en enlevant des caractères spéciaux SANS RIEN DIRE
  2. truncate mon mot de passe auto-généré de 22 caractères SANS RIEN DIRE

Et c'étaiiiiiiit ....... 🥁🥁🥁🥁 un truncate du mot de passe silencieux à 20 caractères

Maintenant retour à la case départ  😭

On notera le ©2014 en footer.  Mais QUI voudrait vous copier ??

+ 3 heures et 6 minutes

[Bonus je-sais-plus-combien] Le code d'erreur qui intègre l'adresse IP (privée) du serveur qui a traité la requête..

Début d'une 4ème tentative (+3h31). Si IE9+Win7 est trop récent, peut-être que WinXP fera l'affaire

La réponse est non

Fin.

« »
 
 
Made with on a hot august night from an airplane the 19th of March 2017.