Pourquoi Rust est-il le nouveau standard de sécurité sur le web ?
\nQuand il s'agit de sécurité web, de nombreux développeurs pensent immédiatement aux ORM, aux requêtes préparées et à l'échappement HTML. Mais que diriez-vous si le langage de programmation lui-même pouvait prendre en charge une grande partie de ce travail ? C'est exactement ce que fait Rust — un langage qui ne pardonne pas les erreurs, mais qui ne permet pas non plus de les commettre.
\n\nDans cet article, nous allons voir comment la sécurité de Rust se manifeste face aux deux attaques les plus critiques sur les applications web : les injections SQL et le XSS (cross-site scripting). Vous verrez des exemples de code réels, une comparaison avec Python et comprendrez pourquoi Rust devient le choix numéro un pour les projets où le prix de l'erreur est la sécurité des utilisateurs.
\n\nComment Rust prévient les injections SQL : de la théorie à la pratique
\nL'injection SQL est un classique. Nous savons tous qu'il faut utiliser des requêtes préparées. Mais dans Rust, ce modèle est intégré à la culture du langage au niveau des types et du système de borrow checker.
\n\nComparaison des approches : Python vs Rust
\nRegardez un code Python typique qui pourrait être vulnérable :
\n# Python (dangereux)\nuser_input = "'; DROP TABLE users; --"\nquery = f"SELECT * FROM users WHERE name = '{user_input}'"\ncursor.execute(query)\n\nMême si vous utilisez psycopg2, vous pourriez accidentellement oublier l'échappement. Rust, quant à lui, vous oblige à penser différemment.
Dans Rust avec la bibliothèque sqlx ou diesel, vous ne pouvez pas simplement insérer une chaîne dans une requête — le système de types ne le permet pas :
// Rust avec sqlx (sécurisé)\nuse sqlx::PgPool;\n\nlet user_input = "'; DROP TABLE users; --";\nlet user: Option<User> = sqlx::query_as(\n "SELECT * FROM users WHERE name = $1"\n)\n.bind(&user_input) // Liaison typée sécurisée\n.fetch_optional(&pool)\n.await?;\n\nRemarquez $1 et .bind(). Rust ne permet pas d'insérer une valeur directement dans la chaîne de la requête. Ce n'est pas simplement une convention — c'est une condition obligatoire à la compilation. La sécurité de Rust se manifeste ici par le fait que vous ne pouvez physiquement pas créer une injection SQL si vous utilisez les bonnes bibliothèques.
Pourquoi cela fonctionne-t-il au niveau des types ?
\nDans Rust, la chaîne de requête et les données sont de types différents. Les bibliothèques comme sqlx vérifient les requêtes à l'étape de compilation (compile-time checked queries). Si vous essayez de faire quelque chose de dangereux, le code ne compilera tout simplement pas :
// Cela ne compilera pas !\nlet query = format!("SELECT * FROM users WHERE name = '{}'", user_input);\nsqlx::query(&query); // Erreur : &str attendu, pas String avec substitution\n\nLe système de possession (ownership) et d'emprunt (borrowing) ne vous permet pas d'"avaler" accidentellement des données dangereuses. Ce n'est pas de la magie — c'est une discipline d'ingénierie intégrée dans le langage.
\n\nProtection contre XSS : échappement et vérification automatique
\nXSS, c'est quand un attaquant injecte des scripts dans votre HTML. Avec Rust, vous pouvez vous protéger à plusieurs niveaux, certaines vérifications ayant lieu avant même l'exécution du programme.
\n\nMoteurs de templates avec échappement automatique
\nEn Python, vous utilisez jinja2 avec autoescape=true. En Rust, il y a askama ou maud, qui font la même chose, mais avec une sécurité de type. Voici un exemple avec askama :
// Rust avec askama (moteur de template)\nuse askama::Template;\n\n#[derive(Template)]\n#[template(source = "<h1>Bonjour, {{ name }} !</h1>", ext = "html")]\nstruct HelloTemplate {\n name: String,\n}\n\nfn main() {\n let t = HelloTemplate {\n name: "<script>alert('XSS')</script>".to_string(),\n };\n let rendered = t.render().unwrap();\n // Sortie : <h1>Bonjour, <script>...</h1>\n // Les balises sont échappées automatiquement !\n}\n\nRemarquez en bas