Applikationslösenord i WordPress: koppla appar och lös 401-felet
Har WordPress-adminet börjat visa en varning under Verktyg, Webbplatshälsa om att "auktoriseringshuvudet saknas"? Eller har en app, ett Zapier-flöde eller en egen integration mot WordPress plötsligt börjat svara med 401 Unauthorized, trots att du är säker på att lösenordet är rätt inklistrat? Båda situationerna handlar oftast om samma sak, applikationslösenord (Application Passwords) och hur webbservern hanterar auktoriseringshuvudet (Authorization header) mellan webbläsaren och PHP.
Det viktigaste i korthet, innan detaljerna:
- Applikationslösenord är en inbyggd WordPress-funktion sedan version 5.6 (november 2020) som låter dig koppla appar och automation till sajten utan att dela ditt vanliga huvudlösenord.
- Funktionen kräver HTTPS som standard. Kör sajten över osäkrad http:// stängs den av helt.
- Du skapar ett applikationslösenord under Användare, Redigera användare, längst ner på sidan. Det visas bara en gång.
- Får du 401 trots att lösenordet är korrekt inklistrat bör du kolla Verktyg, Webbplatshälsa först. WordPress har troligen redan flaggat problemet självt.
- Vanligaste orsaken är att webbservern plockar bort auktoriseringshuvudet innan PHP ser det, ett känt beteende i CGI- och FastCGI-drift, inte ett fel i själva lösenordet.
- Lösningen ser helt olika ut på Apache och Nginx, och kräver ibland åtkomst till serverkonfigurationen som du inte har på delad hosting.
- Jämfört med ett delat huvudlösenord eller den äldre XML-RPC-tekniken ger applikationslösenord bättre spårbarhet. Varje app får ett eget, namngivet och återkallningsbart lösenord.
- Byter du huvudlösenordet påverkas inte applikationslösenorden. De lever separat och måste återkallas för hand.
Så upptäcker du problemet: WordPress egen varning i Webbplatshälsa
Innan du felsöker något själv är det värt att kolla om WordPress redan har gjort jobbet åt dig. Under Verktyg, Webbplatshälsa, Status kör WordPress ett antal automatiska kontroller av sajtens tekniska hälsa, grupperade under rubriker som Prestanda och Säkerhet. En av dem testar specifikt om auktoriseringshuvudet når fram till PHP, ett test som körs asynkront mot ändpunkten wp-site-health/v1/tests/authorization-header efter att sidan laddats klart, vilket är anledningen till att statusen ibland dyker upp med någon sekunds fördröjning.
Är allt som det ska visas en grön rad med texten "The Authorization header is working as expected". Är något fel visas i stället "The Authorization header is missing" eller "The Authorization header is invalid" under Säkerhet. Enligt WordPress egen referens för Site Health beskrivs testet så här, i den engelska originaltexten som visas i adminet: "The Authorization header is used by third-party applications you have approved for this site. Without this header, those apps cannot connect to your site."
Testet har två begränsningar värda att känna till innan du drar slutsatser av det. Ligger sajten redan bakom en generell HTTP Basic Auth, till exempel en lösenordsskyddad staging- eller testmiljö, döljs testet helt, vilket förklarar varför vissa aldrig ser varningen även om servern i grunden har samma begränsning. Och varningen betyder inte att sajten är hackad, komprometterad eller osäker på något generellt plan. Den pekar bara ut att en specifik funktion, kopplingen mellan tredjepartsappar och REST-API:et, inte fungerar tekniskt just nu.
Vad applikationslösenord är, och varför de slår alternativen
Applikationslösenord är en kärnfunktion i WordPress sedan version 5.6, som släpptes i november 2020. Tanken är enkel. I stället för att en extern app eller ett automationsverktyg behöver ditt riktiga, hemliga lösenord för att prata med sajten via REST-API:et, genererar WordPress ett separat lösenord bara för det ändamålet.
Lösenordet skapas via funktionen wp_generate_password(), är 24 tecken långt och ger ungefär 142 bitars entropi, alltså betydligt starkare än de flesta människor väljer själva. Det visas uppdelat i grupper av tecken för att vara lättare att läsa och kopiera, och lagras helt separat från ditt vanliga kontolösenord. Varje applikationslösenord har ett eget namn som du själv sätter, ett skapad-datum och en tidsstämpel för senast använd, och kan återkallas individuellt utan att det påverkar vare sig ditt huvudlösenord eller andra applikationslösenord du har aktiva. Ett applikationslösenord fungerar heller inte i det vanliga inloggningsformuläret på wp-login.php. Det är byggt uteslutande för programmatisk åtkomst.
Ställer man det mot alternativen blir skillnaden i granularitet tydlig.
| Metod | Granularitet | Återkallningsbart | Spårbarhet |
|---|---|---|---|
| Applikationslösenord | Ett unikt lösenord per app eller integration | Ja, per lösenord, utan att påverka övriga | Eget namn, skapad-datum och senast använd per lösenord |
| Delat huvudlösenord | Samma lösenord för allt, inloggning och integrationer | Nej, byte påverkar alla anslutningar samtidigt | Ingen, all åtkomst syns som samma konto utan uppdelning |
| Äldre XML-RPC-anslutning | Vanligtvis samma kontouppgifter för hela protokollet | Bara genom att stänga av XML-RPC helt | Begränsad, svårt att se vilken klient som gjort vad |
Har sajten fortfarande äldre integrationer som bygger på XML-RPC i stället för REST-API:et finns det egna säkerhetsöverväganden att ta ställning till, det går vi igenom mer i detalj i vår genomgång av äldre XML-RPC-integrationer. Kort sagt handlar det om ett äldre, bredare protokoll som är svårare att begränsa i efterhand jämfört med ett applikationslösenord som är avgränsat från start.
Att Webbplatshälsan visar en säkerhetsrelaterad status kring auktoriseringshuvudet betyder inte att själva funktionen applikationslösenord är en risk i sig. Risken uppstår först om lösenord skapas och sedan glöms bort, aldrig granskas eller aldrig återkallas när en integration tas ur bruk. Vill du läsa mer om hur andra utvecklare bygger integrationer mot funktionen finns en teknisk genomgång i integrationsguiden på make.wordpress.org.
Så skapar och kopplar du ett applikationslösenord
Själva skapandet tar en minut, men det finns några detaljer värda att göra rätt från början, särskilt namngivningen.
- Logga in i wp-admin och gå till Användare, Redigera användare (eller din egen profil om det är ditt eget konto).
- Skrolla längst ner på sidan till sektionen "Applikationslösenord".
- Skriv ett beskrivande namn i fältet, till exempel "Zapier, orderbekräftelser" eller "Make, lagersynk" i stället för något generiskt som bara "app". Ett tydligt namn gör det mycket lättare att veta vad du kan återkalla senare utan att gissa.
- Klicka på "Lägg till nytt applikationslösenord". WordPress genererar då ett 24 tecken långt lösenord som visas i klartext precis en gång.
- Kopiera lösenordet direkt och klistra in det i verktyget du ska koppla. Stänger du rutan utan att kopiera går lösenordet inte att visa igen, då får du skapa ett nytt.
- Klistra in lösenordet med eller utan mellanslagen som WordPress visar det med. Båda varianterna fungerar, mellanslagen är bara till för läsbarheten.
Sajten måste ligga på HTTPS för att funktionen ens ska vara tillgänglig, det är ett hårt krav från WordPress kärna, inte en inställning du kan slå av. Har du fortfarande en sajt utan giltigt SSL-certifikat är det första du behöver lösa innan applikationslösenord över huvud taget blir aktuellt.
Bland de vanligaste verktygen svenska småföretag kopplar mot WordPress på det här sättet finns automationsplattformar som Zapier och Make, WordPress-egna automationspluginet Uncanny Automator, mobilappen för WooCommerce som butiksägare använder för att hantera ordrar på språng, och headless-frontend-lösningar där ett separat gränssnitt hämtar innehåll från WordPress via REST-API:et. Ett enkelt sätt att testa att kopplingen fungerar är att köra ett anrop direkt från kommandoraden.
curl --user "ANVANDARE:LOSENORD" https://dinsajt.se/wp-json/wp/v2/users?context=edit
Fungerar det får du tillbaka ett JSON-svar med användarinformation. Får du i stället ett felmeddelande är det dags att gå vidare till felsökningen nedan, för applikationslösenordet kan inte användas i det vanliga inloggningsformuläret, det är uteslutande gjort för den här typen av programmatiska anrop.
Varför du kan få 401 trots rätt lösenord
Det här är den vanligaste källan till förvirring kring hela funktionen. Du har skapat lösenordet korrekt, kopierat det rätt, och ändå svarar servern med 401 Unauthorized så fort appen eller curl-kommandot försöker autentisera sig.
Orsaken ligger sällan i WordPress eller i själva lösenordet. Kör PHP i CGI-, FastCGI- eller FPM-läge, vilket är standard hos de flesta webbhotell, tar webbservern per default hand om HTTP-autentisering själv innan PHP ens startar, i stället för att skicka det råa auktoriseringshuvudet vidare. Resultatet blir att både $_SERVER['HTTP_AUTHORIZATION'] och funktionen getallheaders() är tomma när WordPress försöker läsa dem, trots att webbläsaren eller curl-anropet skickade huvudet helt korrekt.
Enligt förklaringen i WordPress egen wiki för applikationslösenord beskrivs det så här: "…your web server thinks it's meant to handle HTTP Auth and then just pass the request on to PHP." Poängen är alltså att det handlar om ett transportlagerproblem mellan webbservern och PHP, inte ett fel i kontot, pluginen eller lösenordet. Och det drabbar all Basic Auth mot REST-API:et generellt, inte bara just applikationslösenord.
Differentialdiagnos: är det verkligen huvudet som plockas bort?
Innan du börjar redigera serverkonfiguration är det värt att bekräfta att det verkligen är auktoriseringshuvudet som är boven. Fyra symptom pekar mot fyra olika, ganska väsensskilda orsaker.
| Symptom | Trolig orsak | Hur du bekräftar |
|---|---|---|
| 401 direkt, lösenordet är nykopierat och korrekt | Webbservern plockar bort auktoriseringshuvudet (CGI/FastCGI/FPM-default) | Webbplatshälsans test flaggar "Authorization header" |
| 401 oavsett vad du gör, adressen börjar med http:// | Sajten körs inte över HTTPS, applikationslösenord är avstängda på osäkrad http:// | Kolla adressraden, och att SSL-certifikatet faktiskt tillämpas på just den URL:en du testar mot |
| 401 kommer med en tydlig blockeringssida eller logg, inte ett rent WP-felsvar | Ett säkerhetsplugin med inbyggd brandvägg blockerar anrop mot REST-API:et eller Basic Auth | Kontrollera säkerhetspluginets brandväggslogg för blockerade /wp-json/-anrop |
| GET mot /wp-json/ fungerar, men POST eller PUT ger 401 | Ett plugin som bara filtrerar skrivande anrop, eller en serverregel som skiljer på HTTP-metod | Testa samma autentisering mot en GET-ändpunkt och en POST-ändpunkt, och jämför resultaten |
Det vanligaste misstaget här är att tolka 401 som "fel lösenord" per automatik och börja skapa nya applikationslösenord om och om igen. Ett korrekt lösenord ger fortfarande 401 om auktoriseringshuvudet aldrig når fram till PHP, oavsett hur många gånger du genererar ett nytt.
Åtgärden per webbserver
Att en webbserver stänger av rå vidarebefordran av auktoriseringshuvuden är ofta en medveten säkerhetsdefault i CGI- och FastCGI-drift, snarare än slarv från vare sig WordPress eller webbhotellet. Skyddet finns för att förhindra att skript på delad hosting kan läsa autentiseringsuppgifter som egentligen hör till en annan applikation på samma server. Lösningen ser dock helt olika ut beroende på vilken webbserver som ligger under.
| Webbserver | Vad du lägger till | Var | Begränsning |
|---|---|---|---|
| Apache (mod_setenvif) | <IfModule mod_setenvif> | .htaccess eller ett konfigurationsblock | Kräver modulen mod_setenvif, vanligt aktiverad på delad hosting |
| Apache (mod_rewrite) | RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}], läggs direkt efter RewriteEngine On | .htaccess, i WordPress egna rewrite-block | Kräver mod_rewrite, i praktiken nästan alltid aktiverad om permalänkarna fungerar |
| Apache (mod_proxy_fcgi, PHP-FPM bakom Apache) | CGIPassAuth On | .htaccess eller det virtuella värdblocket | Ett Apache-kärndirektiv sedan version 2.4.13. Täcker proxy_fcgi-fall där de två ovanstående inte alltid räcker till |
| Nginx | fastcgi_pass_header Authorization; | fastcgi-sektionen i server- eller location-blocket | Enligt dokumentationen på nginx.org styr direktivet i första hand svarshuvuden från backend, inte förfrågehuvuden som skickas till PHP. Räcker därför sällan ensamt |
| Nginx (kompletterande, allmän praxis) | fastcgi_param HTTP_AUTHORIZATION $http_authorization; | Samma fastcgi-sektion, tillsammans med include fastcgi_params; | Det är den rad som i praktiken sätter variabeln PHP faktiskt läser. Detta är allmän nginx- och PHP-FPM-praxis, inte något WordPress dokumentation själv anger |
| .htaccess på Nginx | Fungerar inte alls | - | Nginx läser aldrig .htaccess-filer, ändringen måste in i huvudkonfigurationen |
| LiteSpeed | Inget officiellt verifierat direktiv | - | Beror på konfigurationen. Kontakta din leverantör för att bekräfta rätt tillvägagångssätt |
Apache-lösningen går inte att kopiera rakt av och förvänta sig ska fungera "typ likadant" på Nginx. Det är två helt olika serverarkitekturer som hanterar CGI-vidarebefordran på olika sätt. Och att bara spara om permalänkarna i WordPress räcker sällan ensamt på Apache, eftersom standardblocket som genereras då inte innehåller auktoriseringsraden. Den måste läggas till separat. Vill du läsa den officiella dokumentationen i sin helhet finns direktivet CGIPassAuth beskrivet hos httpd.apache.org, fastcgi-modulen hos nginx.org, och WordPress egen felsökningsguide i REST-API:ets frågor och svar hos developer.wordpress.org.
Delad hosting: när du inte kan fixa det själv
Kör sajten på Apache eller LiteSpeed via cPanel eller DirectAdmin äger du normalt .htaccess-filen själv, och kan lägga till raderna ovan direkt utan att behöva kontakta någon. Kör sajten bakom Nginx krävs i stället en ändring i huvudkonfigurationen, en fil du som kund på delad hosting oftast inte har åtkomst till. Då återstår ett supportärende, och i WordPress egen kod för applikationslösenord konstateras det rakt av: "You may need to contact your hosting provider for further assistance."
Ett kort, konkret meddelande brukar ge snabbare hjälp än att bara skriva "det funkar inte".
Hej, jag får 401 Unauthorized när jag försöker använda WordPress applikationslösenord mot REST-API:et. Webbplatshälsan i WordPress flaggar att auktoriseringshuvudet inte når fram till PHP. Kan ni lägga till fastcgi_pass_header Authorization och fastcgi_param HTTP_AUTHORIZATION $http_authorization i fastcgi-konfigurationen för min sajt (om ni kör Nginx), eller bekräfta att CGIPassAuth är aktiverat (om ni kör Apache)?
Hur ser det då ut hos svenska delade webbhotell rent konkret? De flesta kör Apache eller LiteSpeed via cPanel eller DirectAdmin, vilket innebär att du normalt äger .htaccess-filen och kan göra ändringen själv. Renodlad Nginx-drift är förhållandevis ovanligt på den svenska delade hostingmarknaden.
- Oderland kör LiteSpeed med cPanel som kontrollpanel.
- Loopia kör Apache 2.4 bakom sin egen kontrollpanel.
- Websupport (tidigare Binero) kör Apache 2.4 bakom sin egen kontrollpanel.
- Wopsa kör Apache med cPanel som kontrollpanel.
Alla fyra tillåter alltså redigering av .htaccess, vilket gör Apache-lösningarna i tabellen ovan direkt tillämpbara. Bilden är dock inte helt entydig hos alla leverantörer. Inleed varierar beroende på paket, deras Standard- och Special-paket kör Apache eller Nginx medan Prime och Ultra kör LiteSpeed med DirectAdmin, så du behöver kontrollera vilket paket just ditt konto ligger på innan du utgår från en viss lösning. one.com kör Apache bakom en egen kontrollpanel, men har medvetet stängt av vissa .htaccess-funktioner, vilket kan innebära att du ändå behöver ett supportärende trots att webbservern i grunden är Apache. GleSYS erbjuder ingen klassisk delad hosting, bara VPS, dedikerade servrar och managed-lösningar, vilket betyder att du där normalt har root-åtkomst själv och situationen ser helt annorlunda ut. Och hos Miss Hosting går det inte att belägga officiellt vilken webbserver som körs bakom cPanel-panelen, så där är rätt väg helt enkelt att fråga supporten direkt i stället för att gissa.
En administratör som stänger av rå vidarebefordran av auktoriseringshuvuden gör det oftast medvetet, som ett skydd mot att andra skript på samma server ska kunna läsa autentiseringsuppgifter som inte är deras. Det är inte per automatik ett tecken på dålig hosting eller slarv, även om det kräver ett extra steg från din sida för att lösa.
Underhåll: granska och återkalla applikationslösenord regelbundet
Listan över dina aktiva applikationslösenord hittar du på samma plats du skapade dem, under Användare, Redigera användare. Där syns namnet du gav varje lösenord, när det skapades, och när det senast användes. Fältet "Senast använd" uppdateras bara en gång per dygn, så det visar rätt dag men kan verka missvisande om du tittar timme för timme och förväntar dig en färsk tidsstämpel direkt efter ett anrop.
En bra vana är att gå igenom listan med jämna mellanrum och återkalla lösenord för appar och integrationer du slutat använda. Har du bytt automationsverktyg, avslutat ett samarbete med en byrå, eller bytt bort en tjänst som tidigare hade en egen koppling, är ett kvarglömt applikationslösenord en onödig öppen dörr, även om risken i sig är låg så länge lösenordet inte läckt.
Det här blir extra viktigt om sajten någon gång drabbas av ett intrång. Applikationslösenord kringgår ett vanligt byte av huvudlösenordet helt, eftersom de lagras separat och lever vidare oavsett vad du gör med kontots ordinarie lösenord. Är du mitt i en sanering efter att ha upptäckt att sajten är komprometterad bör du därför alltid granska och återkalla dina applikationslösenord efter ett intrång som en del av städningen, inte bara byta det vanliga lösenordet och anta att allt är säkrat. Vår genomgång av sanering efter en hackad WordPress-sajt går igenom hela processen steg för steg.
- Gå igenom listan under Användare, Redigera användare, för varje användare med adminrättigheter.
- Återkalla lösenord för appar du inte längre använder eller inte känner igen.
- Skapa nya, tydligt namngivna lösenord för de integrationer som ska vara kvar.
- Upprepa granskningen efter varje misstänkt intrång, oavsett om du redan bytt huvudlösenordet.