Overview
SHA1 Hash: | 0be54823ba2b5afe0b058e77376feaa1ff47751a |
---|---|
Date: | 2008-10-18 12:55:44 |
User: | drh |
Comment: | Add defenses against cross-site request forgery attacks. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/admin.c from [8499f27819] to [bf62e8d4f5].
@@ -76,15 +76,17 @@ style_header("Admin SQL"); @ <h2>SQL:</h2> @ You can enter only SELECT statements here, and some SQL-side functions @ are also restricted.<br/> @ <form action='' method='post'> + login_insert_csrf_secret(); @ <textarea style='border:2px solid black' name='sql' @ cols='80' rows='5'>%h(zSql)</textarea> @ <br/><input type='submit' name='sql_submit'/> <input type='reset'/> @ </form> if( zSql[0] ){ + login_verify_csrf_secret(); sqlite3_set_authorizer(g.db, selectOnly, 0); db_generic_query_view(zSql, 0); sqlite3_set_authorizer(g.db, 0, 0); } style_footer();
Modified src/info.c from [26d96b828e] to [e9965b6667].
@@ -1022,10 +1022,11 @@ if( P("apply") ){ Blob ctrl; char *zDate; int nChng = 0; + login_verify_csrf_secret(); blob_zero(&ctrl); zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&ctrl, "D %s\n", zDate); if( strcmp(zComment,zNewComment)!=0 ){ @@ -1055,10 +1056,11 @@ style_header("Edit Baseline [%s]", zUuid); @ <p>Make changes to the User and Comment for baseline @ [<a href="vinfo?name=%d(rid)">%s(zUuid)</a>] then press the @ "Apply Changes" button.</p> @ <form action="%s(g.zBaseURL)/vedit" method="POST"> + login_insert_csrf_secret(); @ <input type="hidden" name="r" value="%d(rid)"> @ <p> @ <b>User:</b> <input type="text" name="u" size="20" value="%h(zNewUser)"> @ </p> @ <p><b>Comment:</b></b><br />
Modified src/login.c from [d0f9596e5f] to [debcf9a389].
@@ -234,16 +234,16 @@ ** and is valid. If the login cookie checks out, it then sets ** g.zUserUuid appropriately. ** */ void login_check_credentials(void){ - int uid = 0; - const char *zCookie; - const char *zRemoteAddr; - const char *zCap = 0; - const char *zNcap; - const char *zAcap; + int uid = 0; /* User id */ + const char *zCookie; /* Text of the login cookie */ + const char *zRemoteAddr; /* IP address of the requestor */ + const char *zCap = 0; /* Capability string */ + const char *zNcap; /* Capabilities of user "nobody" */ + const char *zAcap; /* Capabllities of user "anonymous" */ /* Only run this check once. */ if( g.userUid!=0 ) return; @@ -255,10 +255,11 @@ if( strcmp(zRemoteAddr, "127.0.0.1")==0 && db_get_int("localauth",0)==0 ){ uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); zCap = "s"; g.noPswd = 1; + strcpy(g.zCsrfToken, "localhost"); } /* Check the login cookie to see if it matches a known valid user. */ if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ @@ -272,18 +273,20 @@ atoi(zCookie), zCookie, zRemoteAddr ); }else if( zCookie[0]=='a' ){ uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"); } + snprintf(g.zCsrfToken, sizeof(g.zCsrfToken), "%.10s", zCookie); } if( uid==0 ){ uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'"); if( uid==0 ){ uid = -1; zCap = ""; } + strcpy(g.zCsrfToken, "none"); } if( zCap==0 ){ if( uid ){ Stmt s; db_prepare(&s, "SELECT login, cap FROM user WHERE uid=%d", uid); @@ -431,6 +434,30 @@ const char *zUrl = PD("REQUEST_URI", "index"); @ <p>Many <font color="red">hyperlinks are disabled.</font><br /> @ Use <a href="%s(g.zTop)/login?anon=1&g=%T(zUrl)">anonymous login</a> @ to enable hyperlinks.</p> } +} + +/* +** While rendering a form, call this routine to add the Anti-CSRF token +** as a hidden element of the form. +*/ +void login_insert_csrf_secret(void){ + @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)"> +} + +/* +** Before using the results of a form, first call this routine to verify +** that ths Anti-CSRF token is present and is valid. If the Anti-CSRF token +** is missing or is incorrect, then this emits and error message and never +** returns. +*/ +void login_verify_csrf_secret(void){ + const char *zCsrf; /* The CSRF secret */ + if( g.okCsrf ) return; + if( (zCsrf = P("csrf"))!=0 && strcmp(zCsrf, g.zCsrfToken)==0 ){ + g.okCsrf = 1; + return; + } + fossil_fatal("Cross-site request forgery attempt"); }
Modified src/main.c from [e3e3bcd32f] to [bc031c08b3].
@@ -118,10 +118,14 @@ int okApndTkt; /* c: append to tickets via the web */ int okWrTkt; /* w: make changes to tickets via web */ int okTktFmt; /* t: create new ticket report formats */ int okRdAddr; /* e: read email addresses or other private data */ int okZip; /* z: download zipped artifact via /zip URL */ + + /* For defense against Cross-site Request Forgery attacks */ + char zCsrfToken[12]; /* Value of the anti-CSRF token */ + int okCsrf; /* Anti-CSRF token is present and valid */ FILE *fDebug; /* Write debug information here, if the file exists */ /* Storage for the aux() and/or option() SQL function arguments */ int nAux; /* Number of distinct aux() or option() values */
Modified src/report.c from [fee58e3fd7] to [e35bb33ea9].
@@ -319,10 +319,11 @@ zOwner = PD("w",g.zLogin); z = P("s"); zSQL = z ? trim_string(z) : 0; zClrKey = trim_string(PD("k","")); if( rn>0 && P("del2") ){ + login_verify_csrf_secret(); db_multi_exec("DELETE FROM reportfmt WHERE rn=%d", rn); cgi_redirect("reportlist"); return; }else if( rn>0 && P("del1") ){ zTitle = db_text(0, "SELECT title FROM reportfmt " @@ -335,10 +336,11 @@ @ <strong>%h(zTitle)</strong> from @ the database. This is an irreversible operation. All records @ related to this report will be removed and cannot be recovered.</p> @ @ <input type="hidden" name="rn" value="%d(rn)"> + login_insert_csrf_secret(); @ <input type="submit" name="del2" value="Delete The Report"> @ <input type="submit" name="can" value="Cancel"> @ </form> style_footer(); return; @@ -354,10 +356,11 @@ zErr = "Please supply a title"; }else{ zErr = verify_sql_statement(zSQL); } if( zErr==0 ){ + login_verify_csrf_secret(); if( rn>0 ){ db_multi_exec("UPDATE reportfmt SET title=%Q, sqlcode=%Q," " owner=%Q, cols=%Q WHERE rn=%d", zTitle, zSQL, zOwner, zClrKey, rn); }else{ @@ -404,10 +407,11 @@ @ <p>Report Title:<br> @ <input type="text" name="t" value="%h(zTitle)" size="60"></p> @ <p>Enter a complete SQL query statement against the "TICKET" table:<br> @ <textarea name="s" rows="20" cols="80">%h(zSQL)</textarea> @ </p> + login_insert_csrf_secret(); if( g.okAdmin ){ @ <p>Report owner: @ <input type="text" name="w" size="20" value="%h(zOwner)"> @ </p> } else {
Modified src/setup.c from [1f3b727596] to [5836f2e874].
@@ -286,10 +286,11 @@ @ @ <p><a href="setup_uedit?id=%d(uid))>[Bummer]</a></p> style_footer(); return; } + login_verify_csrf_secret(); db_multi_exec( "REPLACE INTO user(uid,login,info,pw,cap) " "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')", uid, P("login"), P("info"), zPw, zCap ); @@ -340,10 +341,11 @@ }else{ style_header("Add A New User"); } @ <table align="left" hspace="20" vspace="10"><tr><td> @ <form action="%s(g.zPath)" method="POST"> + login_insert_csrf_secret(); @ <table> @ <tr> @ <td align="right"><nobr>User ID:</nobr></td> if( uid ){ @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td> @@ -543,10 +545,11 @@ zQ = "off"; } if( zQ ){ int iQ = strcmp(zQ,"on")==0 || atoi(zQ); if( iQ!=iVal ){ + login_verify_csrf_secret(); db_set(zVar, iQ ? "1" : "0", 0); iVal = iQ; } } if( iVal ){ @@ -567,10 +570,11 @@ char *zDflt /* Default value if VAR table entry does not exist */ ){ const char *zVal = db_get(zVar, zDflt); const char *zQ = P(zQParm); if( zQ && strcmp(zQ,zVal)!=0 ){ + login_verify_csrf_secret(); db_set(zVar, zQ, 0); zVal = zQ; } @ <input type="text" name="%s(zQParm)" value="%h(zVal)" size="%d(width)"> @ <b>%s(zLabel)</b> @@ -588,10 +592,11 @@ const char *zDflt /* Default value if VAR table entry does not exist */ ){ const char *z = db_get(zVar, (char*)zDflt); const char *zQ = P(zQP); if( zQ && strcmp(zQ,z)!=0 ){ + login_verify_csrf_secret(); db_set(zVar, zQ, 0); z = zQ; } if( rows>0 && cols>0 ){ @ <textarea name="%s(zQP)" rows="%d(rows)" cols="%d(cols)">%h(z)</textarea> @@ -610,11 +615,11 @@ } style_header("Access Control Settings"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/setup_access" method="POST"> - + login_insert_csrf_secret(); @ <hr> onoff_attribute("Require password for local access", "localauth", "localauth", 1); @ <p>When enabled, the password sign-in is required for @ web access coming from 127.0.0.1. When disabled, web access @@ -661,10 +666,11 @@ } style_header("Timeline Display Preferences"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/setup_timeline" method="POST"> + login_insert_csrf_secret(); @ <hr> onoff_attribute("Block markup in timeline", "timeline-block-markup", "tbm", 0); @ <p>In timeline displays, check-in comments can be displayed with or @@ -693,10 +699,11 @@ } style_header("WWW Configuration"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/setup_config" method="POST"> + login_insert_csrf_secret(); @ <hr /> entry_attribute("Project Name", 60, "project-name", "pn", ""); @ <p>Give your project a name so visitors know what this site is about. @ The project name will also be used as the RSS feed title.</p> @ <hr /> @@ -735,10 +742,11 @@ if( !g.okSetup ){ login_needed(); } style_header("Edit CSS"); @ <form action="%s(g.zBaseURL)/setup_editcss" method="POST"> + login_insert_csrf_secret(); @ Edit the CSS:<br /> textarea_attribute("", 40, 80, "css", "css", zDefaultCSS); @ <br /> @ <input type="submit" name="submit" value="Apply Changes"> @ </form> @@ -765,10 +773,11 @@ }else{ textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader); } style_header("Edit Page Header"); @ <form action="%s(g.zBaseURL)/setup_header" method="POST"> + login_insert_csrf_secret(); @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to @ generate the beginning of every page through start of the main @ menu.</p> textarea_attribute("", 40, 80, "header", "header", zDefaultHeader); @ <br /> @@ -799,10 +808,11 @@ }else{ textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter); } style_header("Edit Page Footer"); @ <form action="%s(g.zBaseURL)/setup_footer" method="POST"> + login_insert_csrf_secret(); @ <p>Edit HTML text with embedded TH1 (a TCL dialect) that will be used to @ generate the end of every page.</p> textarea_attribute("", 20, 80, "footer", "footer", zDefaultFooter); @ <br /> @ <input type="submit" name="submit" value="Apply Changes">
Modified src/shun.c from [e38b6e743d] to [5ce55378d6].
@@ -65,10 +65,11 @@ zUuid = zCanonical; } } style_header("Shunned Artifacts"); if( zUuid && P("sub") ){ + login_verify_csrf_secret(); db_multi_exec("DELETE FROM shun WHERE uuid='%s'", zUuid); if( db_exists("SELECT 1 FROM blob WHERE uuid='%s'", zUuid) ){ @ <p><font color="blue">Artifact @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> is no @ longer being shunned.</font></p> @@ -79,10 +80,11 @@ @ <b>fossil rebuild</b> command-line before the artifact content @ can pulled in from other respositories.</font></p> } } if( zUuid && P("add") ){ + login_verify_csrf_secret(); db_multi_exec("INSERT OR IGNORE INTO shun VALUES('%s')", zUuid); @ <p><font color="blue">Artifact @ <a href="%s(g.zBaseURL)/artifact/%s(zUuid)">%s(zUuid)</a> has been @ shunned. It will no longer be pushed. @ It will be removed from the repository the next time the respository @@ -124,10 +126,11 @@ @ of the repository. Do not shun artifacts merely to remove them from @ sight - set the "hidden" tag on such artifacts instead.</p> @ @ <blockquote> @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)"> + login_insert_csrf_secret(); @ <input type="text" name="uuid" value="%h(PD("shun",""))" size="50"> @ <input type="submit" name="add" value="Shun"> @ </form> @ </blockquote> @ @@ -137,10 +140,11 @@ @ the formerly shunned artifact will be accepted on subsequent sync @ operations.</p> @ @ <blockquote> @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)"> + login_insert_csrf_secret(); @ <input type="text" name="uuid" size="50"> @ <input type="submit" name="sub" value="Accept"> @ </form> @ </blockquote> style_footer();
Modified src/tkt.c from [4222b4dfef] to [e980babe0a].
@@ -384,10 +384,11 @@ const char *zUuid; int i; int rid; Blob tktchng, cksum; + login_verify_csrf_secret(); zUuid = (const char *)pUuid; blob_zero(&tktchng); zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&tktchng, "D %s\n", zDate); @@ -467,10 +468,11 @@ ticket_init(); getAllTicketFields(); initializeVariablesFromDb(); initializeVariablesFromCGI(); @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)"> + login_insert_csrf_secret(); zScript = ticket_newpage_code(); Th_Store("login", g.zLogin); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zNewUuid, 0); @@ -528,10 +530,11 @@ getAllTicketFields(); initializeVariablesFromCGI(); initializeVariablesFromDb(); @ <form method="POST" action="%s(g.zBaseURL)/%s(g.zPath)"> @ <input type="hidden" name="name" value="%s(zName)"> + login_insert_csrf_secret(); zScript = ticket_editpage_code(); Th_Store("login", g.zLogin); Th_Store("date", db_text(0, "SELECT datetime('now')")); Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0); Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
Modified src/tktsetup.c from [cc6def603d] to [c8d31b703f].
@@ -119,24 +119,27 @@ if( z==0 ){ z = db_get(zDbField, (char*)zDfltValue); } style_header("Edit %s", zTitle); if( P("clear")!=0 ){ + login_verify_csrf_secret(); db_unset(zDbField, 0); if( xRebuild ) xRebuild(); z = zDfltValue; }else if( isSubmit ){ char *zErr = 0; + login_verify_csrf_secret(); if( xText && (zErr = xText(z))!=0 ){ @ <p><font color="red"><b>ERROR: %h(zErr)</b></font></p> }else{ db_set(zDbField, z, 0); if( xRebuild ) xRebuild(); cgi_redirect("tktsetup"); } } @ <form action="%s(g.zBaseURL)/%s(g.zPath)" method="POST"> + login_insert_csrf_secret(); @ <p>%s(zDesc)</p> @ <textarea name="x" rows="%d(height)" cols="80">%h(z)</textarea> @ <blockquote> @ <input type="submit" name="submit" value="Apply Changes"> @ <input type="submit" name="clear" value="Revert To Default"> @@ -634,10 +637,11 @@ cgi_redirect("tktsetup"); } style_header("Ticket Display On Timelines"); db_begin_transaction(); @ <form action="%s(g.zBaseURL)/tktsetup_timeline" method="POST"> + login_insert_csrf_secret(); @ <hr> entry_attribute("Ticket Title", 40, "ticket-title-expr", "t", "title"); @ <p>An SQL expression in a query against the TICKET table that will @ return the title of the ticket for display purposes after hyperlinks to
Modified src/wiki.c from [6fc1aec89b] to [5bb1f01093].
@@ -251,10 +251,11 @@ blob_zero(&wiki); db_begin_transaction(); if( isSandbox ){ db_set("sandbox",zBody,0); }else{ + login_verify_csrf_secret(); zDate = db_text(0, "SELECT datetime('now')"); zDate[10] = 'T'; blob_appendf(&wiki, "D %s\n", zDate); free(zDate); blob_appendf(&wiki, "L %F\n", zPageName); @@ -300,10 +301,11 @@ if( z[0]=='\n' ) n++; } if( n<20 ) n = 20; if( n>40 ) n = 40; @ <form method="POST" action="%s(g.zBaseURL)/wikiedit"> + login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zPageName)"> @ <textarea name="w" class="wikiedit" cols="80" @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea> @ <br> @ <input type="submit" name="preview" value="Preview Your Changes"> @@ -384,10 +386,11 @@ if( isSandbox ){ blob_appendf(&body, db_get("sandbox","")); appendRemark(&body); db_set("sandbox", blob_str(&body), 0); }else{ + login_verify_csrf_secret(); content_get(rid, &content); manifest_parse(&m, &content); if( m.type==CFTYPE_WIKI ){ blob_append(&body, m.zWiki, -1); } @@ -435,10 +438,11 @@ @ <hr> blob_reset(&preview); } zUser = PD("u", g.zLogin); @ <form method="POST" action="%s(g.zBaseURL)/wikiappend"> + login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zPageName)"> @ Your Name: @ <input type="text" name="u" size="20" value="%h(zUser)"><br> @ Comment to append:<br> @ <textarea name="r" class="wikiedit" cols="80"