Dolibarr/dolibarr · warning

ErrorLoginMustBePostMethod

Error message

ErrorLoginMustBePostMethod

What it means

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.

Solutions

  1. Change the login form to method="post".
  2. Rewrite any custom script to send credentials in the POST body, not the URL.
  3. For legitimate OAuth callback flows, ensure the afteroauthloginreturn parameter is set instead of sending passwords via GET.
  4. Rotate any credentials that already leaked into access logs/browser history; update offending third-party plugins.

Example fix

// before
header('Location: index.php?actionlogin=login&username=u&password=p');
// after
// use a POST form:
echo '<form method="post" action="index.php"><input type="hidden" name="actionlogin" value="login">...</form>';
Defensive patterns

Strategy: validation

Validate before calling

if (isset($_GET['password']) || (isset($_GET['username']) && isset($_GET['actionlogin']))) { die('Credentials must be submitted via POST'); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Dolibarr/dolibarr@598aa4bdad (2026-09-14). Data as JSON: /api/errors/eca3d125d8138e58. Report an issue: GitHub.

Appendix: source

Thrown at htdocs/main.inc.php:763

			$langs->setDefaultLang($langcode);
		}

		// Test HTTP header
		if (!empty($_SERVER['HTTP_EXPOSED_CREDENTIAL_CHECK'])) {
			// TODO Read option $dolibarr_main_no_leaked_credentials with value 1, 2, ... and return
			//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);
			dol_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);
		}

		// Refuse a login submission that carries a password in a GET query string.
		// This avoids the password ending up in web server access logs,
		// the browser history, the Referrer header or any HTTP proxy log (CWE-598).
		// OAuth callbacks legitimately use GET but use afteroauthloginreturn.
		// Other external pluginn using login_hashin GET are also legitimate.
		if (GETPOST('actionlogin', 'aZ09') == 'login' && !GETPOST('afteroauthloginreturn', 'alphanohtml', 1) && GETPOST('password', 'password', 1)) {
			dol_syslog("--- Login submission with credentials in the query string refused for ".$_SERVER["PHP_SELF"], LOG_WARNING);
			$langs->loadLangs(array('main', 'errors'));
			$_SESSION["dol_loginmesg"] = $langs->transnoentitiesnoconv("ErrorLoginMustBePostMethod");
			$test = false;
		}

		// Validation of login/pass/entity
		// If ok, the variable login will be returned
		// If error, we will put error message in session under the name dol_loginmesg
		if ($test && $goontestloop && GETPOST('actionlogin', 'aZ09') != 'disabled' && (GETPOST('actionlogin', 'aZ09') == 'login' || $dolibarr_main_authentication != 'dolibarr')) {
			// Loop on each test mode defined into $authmode
			// $authmode is an array for example: array('0'=>'dolibarr', '1'=>'googleoauth');
			$oauthmodetotestarray = array('google');
			foreach ($oauthmodetotestarray as $oauthmodetotest) {
				if (in_array($oauthmodetotest.'oauth', $authmode)) {	// This is an authmode that is currently qualified. Do we have to remove it ?
					// If we click on the link to use OAuth authentication or if we go here after a callback return, we do nothing
					if (GETPOST('beforeoauthloginredirect') == $oauthmodetotest || GETPOST('afteroauthloginreturn') == $oauthmodetotest) {
						continue;
					}
					dol_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);
					foreach ($authmode as $tmpkey => $tmpval) {

View on GitHub (pinned to 598aa4bdad)