Expansión multilenguaje de detección SQL Injection · Cliente: FinTrack SaaS · Go + Java desde regla Python original
ruleid:) y seguros (ok:) antes de la regla.semgrep --test con 100% tests pasados. Sin excepciones.database/sqldb.Query(), db.Exec(), db.QueryRow()r.URL.Query().Get(), r.FormValue()flask.request — adaptar a net/http? placeholderStatement.executeQuery()executeQuery, executeUpdate, executeHttpServletRequest.getParameter()(Statement $S) para evitar FP con PreparedStatementPreparedStatement = segurorules: - id: sql-injection-golang languages: [go] severity: ERROR message: > SQL Injection potencial: query construida concatenando entrada de usuario. Usa prepared statements con placeholders. metadata: category: security cwe: CWE-89 owasp: A03:2021 confidence: HIGH mode: taint pattern-sources: - pattern: $R.URL.Query().Get(...) - pattern: $R.FormValue(...) - pattern: $R.PostFormValue(...) - pattern: $R.Header.Get(...) pattern-sinks: - pattern: $DB.Query($QUERY, ...) - pattern: $DB.QueryRow($QUERY, ...) - pattern: $DB.Exec($QUERY, ...) - pattern: $DB.QueryContext($CTX, $QUERY, ...) - pattern: $DB.ExecContext($CTX, $QUERY, ...) pattern-sanitizers: - pattern: | $DB.Prepare(...) - pattern: fmt.Sprintf("...", ...) # solo cuando no hay concatenación
package main import ( "database/sql" "net/http" ) // CASO VULNERABLE 1: concatenación directa // ruleid: sql-injection-golang func getUser(db *sql.DB, r *http.Request) { id := r.URL.Query().Get("id") query := "SELECT * FROM users WHERE id = " + id db.Query(query) } // CASO VULNERABLE 2: fmt.Sprintf con input // ruleid: sql-injection-golang func getUserFmt(db *sql.DB, r *http.Request) { name := r.FormValue("name") q := fmt.Sprintf( "SELECT * FROM accounts WHERE name='%s'", name) db.Exec(q) } // CASO SEGURO 1: prepared statement con ? // ok: sql-injection-golang func getUserSafe(db *sql.DB, r *http.Request) { id := r.URL.Query().Get("id") db.Query("SELECT * FROM users WHERE id = ?", id) } // CASO SEGURO 2: Prepare + Exec // ok: sql-injection-golang func getUserPrepare(db *sql.DB, r *http.Request) { name := r.FormValue("name") stmt, _ := db.Prepare( "SELECT * FROM accounts WHERE name = ?") stmt.Exec(name) }
rules: - id: sql-injection-java languages: [java] severity: ERROR message: > SQL Injection: query construida con String.format o concatenación directa. Migra a PreparedStatement con parámetros. metadata: category: security cwe: CWE-89 owasp: A03:2021 confidence: HIGH mode: taint pattern-sources: - pattern: | $REQ.getParameter(...) - pattern: | $REQ.getHeader(...) - pattern: | $REQ.getQueryString() pattern-sinks: - pattern: | (Statement $S).executeQuery($Q) - pattern: | (Statement $S).executeUpdate($Q) - pattern: | (Statement $S).execute($Q) - pattern: | (Statement $S).executeLargeUpdate($Q) pattern-sanitizers: - pattern: | $CONN.prepareStatement(...) - pattern: | StringEscapeUtils.escapeSql(...)
import java.sql.*; import javax.servlet.http.*; public class UserDAO { // CASO VULNERABLE 1: concatenación JDBC // ruleid: sql-injection-java public void getUser(Connection conn, HttpServletRequest req) throws SQLException { String id = req.getParameter("id"); Statement stmt = conn.createStatement(); stmt.executeQuery( "SELECT * FROM users WHERE id = " + id); } // CASO VULNERABLE 2: String.format en sink // ruleid: sql-injection-java public void getByName(Connection conn, HttpServletRequest req) throws SQLException { String name = req.getParameter("name"); Statement stmt = conn.createStatement(); stmt.executeUpdate(String.format( "UPDATE accounts SET last_seen=NOW() WHERE name='%s'", name)); } // CASO SEGURO 1: PreparedStatement // ok: sql-injection-java public void getUserSafe(Connection conn, HttpServletRequest req) throws SQLException { String id = req.getParameter("id"); PreparedStatement ps = conn.prepareStatement( "SELECT * FROM users WHERE id = ?"); ps.setString(1, id); ps.executeQuery(); } // CASO SEGURO 2: query hardcoded, sin taint // ok: sql-injection-java public void getAllUsers(Connection conn) throws SQLException { Statement stmt = conn.createStatement(); stmt.executeQuery("SELECT * FROM users"); } }
rules: - id: sql-injection-python languages: [python] severity: ERROR mode: taint pattern-sources: - pattern: flask.request.args.get(...) - pattern: flask.request.form.get(...) - pattern: flask.request.json pattern-sinks: - pattern: $CURSOR.execute($QUERY, ...) - pattern: $CURSOR.executemany($QUERY, ...) pattern-sanitizers: - pattern: sqlalchemy.text(...)
# Dumped AST para análisis de patrones $ semgrep --dump-ast -l go \ sql-injection-golang.go # Validar YAML $ semgrep --validate \ --config sql-injection-golang.yaml No errors. 1 rule validated. # Ejecutar tests $ semgrep --test \ --config sql-injection-golang.yaml \ sql-injection-golang.go All tests passed.
# Dumped AST para análisis de patrones $ semgrep --dump-ast -l java \ sql-injection-java.java # Validar YAML $ semgrep --validate \ --config sql-injection-java.yaml No errors. 1 rule validated. # Ejecutar tests $ semgrep --test \ --config sql-injection-java.yaml \ sql-injection-java.java All tests passed.