{"record":{"id":"eca3d125d8138e58","repo":"Dolibarr/dolibarr","slug":"errorloginmustbepostmethod","errorCode":null,"errorMessage":"ErrorLoginMustBePostMethod","messagePattern":"ErrorLoginMustBePostMethod","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"htdocs/main.inc.php","lineNumber":763,"sourceCode":"\t\t\t$langs->setDefaultLang($langcode);\n\t\t}\n\n\t\t// Test HTTP header\n\t\tif (!empty($_SERVER['HTTP_EXPOSED_CREDENTIAL_CHECK'])) {\n\t\t\t// TODO Read option $dolibarr_main_no_leaked_credentials with value 1, 2, ... and return\n\t\t\t//dol_syslog(\"--- Access to \".(empty($_SERVER[\"REQUEST_METHOD\"]) ? '' : $_SERVER[\"REQUEST_METHOD\"].' ').$_SERVER[\"PHP_SELF\"].' refused by option $dolibarr_main_no_leaked_credentials='.$dolibarr_main_no_leaked_credentials, LOG_NOTICE);\n\t\t\tdol_syslog('--- Security warning: credentials reported as leaked were used to try to login. HTTP_EXPOSED_CREDENTIAL_CHECK='.((int) $_SERVER['HTTP_EXPOSED_CREDENTIAL_CHECK']), LOG_NOTICE);\n\t\t}\n\n\t\t// Refuse a login submission that carries a password in a GET query string.\n\t\t// This avoids the password ending up in web server access logs,\n\t\t// the browser history, the Referrer header or any HTTP proxy log (CWE-598).\n\t\t// OAuth callbacks legitimately use GET but use afteroauthloginreturn.\n\t\t// Other external pluginn using login_hashin GET are also legitimate.\n\t\tif (GETPOST('actionlogin', 'aZ09') == 'login' && !GETPOST('afteroauthloginreturn', 'alphanohtml', 1) && GETPOST('password', 'password', 1)) {\n\t\t\tdol_syslog(\"--- Login submission with credentials in the query string refused for \".$_SERVER[\"PHP_SELF\"], LOG_WARNING);\n\t\t\t$langs->loadLangs(array('main', 'errors'));\n\t\t\t$_SESSION[\"dol_loginmesg\"] = $langs->transnoentitiesnoconv(\"ErrorLoginMustBePostMethod\");\n\t\t\t$test = false;\n\t\t}\n\n\t\t// Validation of login/pass/entity\n\t\t// If ok, the variable login will be returned\n\t\t// If error, we will put error message in session under the name dol_loginmesg\n\t\tif ($test && $goontestloop && GETPOST('actionlogin', 'aZ09') != 'disabled' && (GETPOST('actionlogin', 'aZ09') == 'login' || $dolibarr_main_authentication != 'dolibarr')) {\n\t\t\t// Loop on each test mode defined into $authmode\n\t\t\t// $authmode is an array for example: array('0'=>'dolibarr', '1'=>'googleoauth');\n\t\t\t$oauthmodetotestarray = array('google');\n\t\t\tforeach ($oauthmodetotestarray as $oauthmodetotest) {\n\t\t\t\tif (in_array($oauthmodetotest.'oauth', $authmode)) {\t// This is an authmode that is currently qualified. Do we have to remove it ?\n\t\t\t\t\t// If we click on the link to use OAuth authentication or if we go here after a callback return, we do nothing\n\t\t\t\t\tif (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn') == $oauthmodetotest) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tdol_syslog(\"User did not click on link for OAuth mode \".$oauthmodetotest.\", param beforeoauthloginredirect is \".GETPOST('beforeoauthloginredirect').\" and param afteroauthloginreturn is \".GETPOST('afteroauthloginreturn').\" so we disable check of login for mode \".$oauthmodetotest);\n\t\t\t\t\tforeach ($authmode as $tmpkey => $tmpval) {","sourceCodeStart":745,"sourceCodeEnd":781,"githubUrl":"https://github.com/Dolibarr/dolibarr/blob/598aa4bdada683d17ca04b1842548821ff0eb6c6/htdocs/main.inc.php#L745-L781","documentation":"Dolibarr refuses login submissions where the password arrives in the query string via GET, to keep passwords out of browser history, Referrer headers and proxy logs (CWE-598). The only GET exception is OAuth callbacks flagged by afteroauthloginreturn. The login form must submit via POST.","triggerScenarios":"A form or script builds a URL like index.php?actionlogin=login&username=x&password=y and requests it with GET; GETPOST('password') present while actionlogin=login and no afteroauthloginreturn parameter.","commonSituations":"Custom login/SSO scripts hand-crafting GET login URLs; links saved in browser history from a misconfigured form with method=GET; third-party plugins performing GET-based logins that should migrate to POST or the OAuth path.","solutions":["Change the login form to method=\"post\".","Rewrite any custom script to send credentials in the POST body, not the URL.","For legitimate OAuth callback flows, ensure the afteroauthloginreturn parameter is set instead of sending passwords via GET.","Rotate any credentials that already leaked into access logs/browser history; update offending third-party plugins."],"exampleFix":"// before\nheader('Location: index.php?actionlogin=login&username=u&password=p');\n// after\n// use a POST form:\necho '<form method=\"post\" action=\"index.php\"><input type=\"hidden\" name=\"actionlogin\" value=\"login\">...</form>';","handlingStrategy":"validation","validationCode":"if (isset($_GET['password']) || (isset($_GET['username']) && isset($_GET['actionlogin']))) { die('Credentials must be submitted via POST'); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always use POST forms for authentication UIs","Scan access logs for credentials in query strings and rotate them","Test integrations with a proxy to confirm no secrets in URLs"],"tags":["dolibarr","security","http-method"],"backgroundTag":"invalid-http-method","analyzedSha":"598aa4bdada683d17ca04b1842548821ff0eb6c6","analyzedAt":"2026-09-14T11:12:15.309Z","contentChangedAt":"2026-09-14T11:12:15.309Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}