3200d39bfd8e7c797f2d1d24e3c9567903da7f68 max Tue May 27 16:51:10 2025 -0700 Revert "adding cloudflare bot protection, refs #35790" This reverts commit 27370697e01a3f1e80c90bb9a003560b959beed4. diff --git src/hg/lib/cart.c src/hg/lib/cart.c index 471adae1e1e..1a769655b32 100644 --- src/hg/lib/cart.c +++ src/hg/lib/cart.c @@ -26,32 +26,30 @@ #endif /* GBROWSE */ #include "hgMaf.h" #include "hui.h" #include "geoMirror.h" #include "hubConnect.h" #include "trackHub.h" #include "cgiApoptosis.h" #include "customComposite.h" #include "regexHelper.h" #include "windowsToAscii.h" #include "jsonWrite.h" #include "verbose.h" #include "genark.h" #include "quickLift.h" -#include <curl/curl.h> - static char *sessionVar = "hgsid"; /* Name of cgi variable session is stored in. */ static char *positionCgiName = "position"; DbConnector cartDefaultConnector = hConnectCart; DbDisconnect cartDefaultDisconnector = hDisconnectCart; static boolean cartDidContentType = FALSE; struct slPair *httpHeaders = NULL; // A list of headers to output before the content-type static void hashUpdateDynamicVal(struct hash *hash, char *name, void *val) /* Val is a dynamically allocated (freeMem-able) entity to put * in hash. Override existing hash item with that name if any. * Otherwise make new hash item. */ { struct hashEl *hel = hashLookup(hash, name); @@ -1440,195 +1438,64 @@ else { char *url = genarkUrl(db); if (url != NULL) { cartSetString(cart, "genome", db); cartAddString(cart, "hubUrl", url); cartRemove(cart, "db"); } else if (!hDbIsActive(db)) errAbort("Can not find database '%s'", db); } } -// ------ libify this in the next release ---- -// -struct curlString { - char *ptr; - size_t len; -}; -void init_string(struct curlString *s) { - s->len = 0; - s->ptr = malloc(1); - s->ptr[0] = '\0'; -} - -size_t writefunc(void *ptr, size_t size, size_t nmemb, void *userData) { - struct curlString *s = (struct curlString *)userData; - size_t new_len = s->len + size * nmemb; - s->ptr = realloc(s->ptr, new_len + 1); - memcpy(s->ptr + s->len, ptr, size * nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - return size * nmemb; -} - -char* curlPostUrl(char *url, char *data) -/* post data to URL and return as string. Must be freed. */ -{ -CURL *curl = curl_easy_init(); -if (!curl) - errAbort("Cannot init curl library"); - -struct curlString response; -init_string(&response); - -curl_easy_setopt(curl, CURLOPT_URL, url); -curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); -curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); -curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); -curl_easy_perform(curl); -curl_easy_cleanup(curl); - -char *resp = cloneString(response.ptr); -free(response.ptr); -return resp; -} - -boolean isValidToken(char *token) -/* send https req to cloudflare, check if the token that we got from the captcha is really the one made by cloudflare */ -{ - char *url = "https://challenges.cloudflare.com/turnstile/v0/siteverify"; - char *secret = cfgVal("cloudFlareSecretKey"); - if (!secret) - errAbort("'cloudFlareSecretKey' must be set in hg.conf if cloudflare is activated in hg.conf"); - - char data[3000]; // cloudflare token is at most 2000 bytes - safef(data, sizeof(data), "secret=%s&response=%s", secret, token); - char *reply = curlPostUrl(url, data); - - boolean res = startsWith("{\"success\":true", reply); - freez(&reply); - return res; -} - -#define CLOUDFLARESITEKEY "cloudFlareSiteKey" - -void printCaptcha() -/* print an html page that shows the captcha and on success, reloads the page with the token added as token=x */ -{ - char *cfSiteKey = cfgVal(CLOUDFLARESITEKEY); - if (!cfSiteKey) - return; - - puts("Content-Type:text/html"); - puts("\n"); - puts("<html><head>"); - puts("<script>"); - printf("function showWidget() { \n" - "turnstile.render('#myWidget', {\n" - "sitekey: '%s',\n" - "theme: 'light',\n" - "callback: function (token) {\n" - " const parser = new URL(window.location);\n" - " parser.searchParams.set('token', token);\n" - " window.location = parser.href;\n" - " },\n" - "});\n" - "}\n", cfSiteKey); - puts("</script>"); - puts("</head><body>"); - puts("<style>body, h1, h2, h3, h4, h5, h6 { font-family: Helvetica, Arial, sans-serif; }</style>\n"); - puts("<h4>The Genome Browser is protecting itself from bots. This will just take a few seconds.</h4>"); - puts("<small>If you are a bot and were made for a research project, please contact us by email.</small>"); - puts("<script src='https://challenges.cloudflare.com/turnstile/v0/api.js?onload=showWidget' async defer></script>"); - puts("<div id='myWidget'></div>"); - puts("</body></html>"); - sqlCleanupAll(); // we are wondering about hanging connections, so just in case, close them. - exit(0); -} - -void forceUserIdOrCaptcha(struct cart* cart, char *userId, boolean userIdFound, boolean fromCommandLine) -/* print captcha is user did not sent a valid hguid cookie or a valid cloudflare token. Always allow rtracklayer. */ -{ -if (fromCommandLine || !cfgOption(CLOUDFLARESITEKEY)) - return; - -if (!cfgOption("blockRtracklayer") && sameOk(cgiUserAgent(), "rtracklayer")) - return; - -// so QA can add a user agent after release, in case someone complains -char *okUserAgent = cfgOption("okUserAgent"); -if (okUserAgent && sameOk(cgiUserAgent(), okUserAgent)) - return; - -if (userId && userIdFound) - return; - -char *token = cgiOptionalString("token"); - -if (token && isValidToken(token)) -{ - cartRemove(cart, "token"); - return; -} - -printCaptcha(); -} - -void cartRemove(struct cart *cart, char *var); - struct cart *cartNew(char *userId, char *sessionId, char **exclude, struct hash *oldVars) /* Load up cart from user & session id's. Exclude is a null-terminated list of * strings to not include */ { cgiApoptosisSetup(); if (cfgOptionBooleanDefault("showEarlyErrors", TRUE)) errAbortSetDoContentType(TRUE); if (cfgOptionBooleanDefault("suppressVeryEarlyErrors", FALSE)) htmlSuppressErrors(); setUdcCacheDir(); netSetTimeoutErrorMsg("A connection timeout means that either the server is offline or its firewall, the UCSC firewall or any router between the two blocks the connection."); + struct cart *cart; struct sqlConnection *conn = cartDefaultConnector(); char *ex; boolean userIdFound = FALSE, sessionIdFound = FALSE; AllocVar(cart); cart->hash = newHash(12); cart->exclude = newHash(7); cart->userId = userId; cart->sessionId = sessionId; cart->userInfo = loadDb(conn, userDbTable(), userId, &userIdFound); - cart->sessionInfo = loadDb(conn, sessionDbTable(), sessionId, &sessionIdFound); -boolean fromCli = cgiWasSpoofed(); // QA runs our CGIs from the command line and we debug from there +boolean fromCli = cgiWasSpoofed(); -forceUserIdOrCaptcha(cart, userId, userIdFound, fromCli); +if (sessionIdFound && !userIdFound && !fromCli && cfgOptionBooleanDefault("cartTrace", FALSE)) + fprintf(stderr, "HGSID_WITHOUT_COOKIE\n"); -// we rely on the cookie being validated, so if we reset a cookie, do this after the captcha -if ( cgiOptionalString("ignoreCookie") != NULL ) - cart->userInfo = loadDb(conn, userDbTable(), NULL, &userIdFound); - -// Leaving this in the code temporarily, until June 2025 release. if (!fromCli && ((sessionId && !sessionIdFound) || !sessionId) && (!userId || !userIdFound) && cfgOptionBooleanDefault("punishInvalidHgsid", FALSE)) { fprintf(stderr, "HGSID_WAIT no sessionId and no cookie: 5 seconds penalty"); sleep(5); if (sessionId && !sessionIdFound) { fprintf(stderr, "HGSID_WAIT2 sessionId sent but invalid: 10 seconds penalty"); sleep(10); } } if (sessionIdFound) @@ -2643,31 +2510,35 @@ char *noProxy = cfgOption("noProxy"); if (noProxy) setenv("no_proxy", noProxy, TRUE); char *logProxy = cfgOption("logProxy"); if (logProxy) setenv("log_proxy", logProxy, TRUE); /* noSqlInj settings so they are accessible in src/lib too */ char *noSqlInj_level = cfgOption("noSqlInj.level"); if (noSqlInj_level) setenv("noSqlInj_level", noSqlInj_level, TRUE); char *noSqlInj_dumpStack = cfgOption("noSqlInj.dumpStack"); if (noSqlInj_dumpStack) setenv("noSqlInj_dumpStack", noSqlInj_dumpStack, TRUE); -char *hguid = getCookieId(cookieName); + +// if ignoreCookie is on the URL, don't check for cookies +char *hguid = NULL; +if ( cgiOptionalString("ignoreCookie") == NULL ) + hguid = getCookieId(cookieName); // if _dumpToLog is on the URL, we can exit early with whatever // message we are trying to write to the stderr/error_log char *logMsg = NULL; if ( (logMsg = cgiOptionalString("_dumpToLog")) != NULL) { cartJsonStart(); fprintf(stderr, "%s", logMsg); cartJsonEnd(NULL); exit(0); } char *hgsid = getSessionId(); struct cart *cart = cartNew(hguid, hgsid, exclude, oldVars); cartExclude(cart, sessionVar);