-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path5fb60759.be50d29a.js
1 lines (1 loc) · 9.61 KB
/
5fb60759.be50d29a.js
1
"use strict";(self.webpackChunkadminforth=self.webpackChunkadminforth||[]).push([[5859],{3885:(e,n,s)=>{s.r(n),s.d(n,{assets:()=>d,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"tutorial/Customization/security","title":"Security","description":"Security and privacy if adminforth users is one of the most important aspects of AdminForth.","source":"@site/docs/tutorial/03-Customization/12-security.md","sourceDirName":"tutorial/03-Customization","slug":"/tutorial/Customization/security","permalink":"/docs/tutorial/Customization/security","draft":false,"unlisted":false,"tags":[],"version":"current","sidebarPosition":12,"frontMatter":{},"sidebar":"tutorialSidebar","previous":{"title":"Data API","permalink":"/docs/tutorial/Customization/dataApi"},"next":{"title":"Standard pages tuning","permalink":"/docs/tutorial/Customization/standardPagesTuning"}}');var t=s(4848),i=s(8453);const o={},a="Security",d={},l=[{value:"How long does user login last?",id:"how-long-does-user-login-last",level:2},{value:"Password strength",id:"password-strength",level:2},{value:"Trusting client IP addresses",id:"trusting-client-ip-addresses",level:2},{value:"Using proxing CDNs",id:"using-proxing-cdns",level:3},{value:"Using reverse proxy",id:"using-reverse-proxy",level:3}];function c(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,i.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.header,{children:(0,t.jsx)(n.h1,{id:"security",children:"Security"})}),"\n",(0,t.jsx)(n.p,{children:"Security and privacy if adminforth users is one of the most important aspects of AdminForth."}),"\n",(0,t.jsx)(n.h2,{id:"how-long-does-user-login-last",children:"How long does user login last?"}),"\n",(0,t.jsxs)(n.p,{children:["By default after authentication user session lasts for 24 hours. After that user is redirected to login page.\nYou can tweak login cookie expiration time by setting environment ",(0,t.jsx)(n.code,{children:"ADMINFORTH_AUTH_EXPIRESIN"}),". For example to set it to 1 hour:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-bash",children:"ADMINFORTH_AUTH_EXPIRESIN=1h\n"})}),"\n",(0,t.jsxs)(n.p,{children:["Also you can set ",(0,t.jsx)(n.code,{children:"auth.rememberMeDays"}),' in the config to set how long "remember me" logins will last.\nFor example to set it to 7 days:']}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-ts",metastring:"./index.ts",children:"new AdminForth({\n ...\n auth: {\n rememberMeDays: 7\n }\n}\n"})}),"\n",(0,t.jsx)(n.p,{children:'In this case users who will check "Remember me" checkbox will be logged in for 7 days instead of 24 hours.'}),"\n",(0,t.jsx)(n.h2,{id:"password-strength",children:"Password strength"}),"\n",(0,t.jsxs)(n.p,{children:["AdminForth allows to set validation RegExp based rules for any field. This can be reused for password strength validation.\n",(0,t.jsx)(n.a,{href:"/docs/tutorial/gettingStarted",children:"Getting started"})," guide suggests you to set next parameters for password field:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-ts",metastring:"./index.ts",children:" minLength: 8,\n validation: [\n AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM,\n ],\n"})}),"\n",(0,t.jsx)(n.p,{children:"So when admin user will create another user, password will be validated against next rules:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"At least 8 characters"}),"\n",(0,t.jsx)(n.li,{children:"At least one uppercase letter"}),"\n",(0,t.jsx)(n.li,{children:"At least one lowercase letter"}),"\n",(0,t.jsx)(n.li,{children:"At least one number"}),"\n"]}),"\n",(0,t.jsx)(n.p,{children:"For improving requirement you might also request special character:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-ts",metastring:"./index.ts",children:" minLength: 8,\n validation: [\n AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM_SPECIAL\n ],\n"})}),"\n",(0,t.jsx)(n.p,{children:"Also you can add custom rules. For example to prevent popular words:"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-ts",metastring:"./index.ts",children:" minLength: 8,\n validation: [\n {\n regExp: '^(?!.*(?:qwerty|password|user|login|qwerty|123456)).*$',\n message: 'Password cannot contain easily guessed words'\n },\n AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM_SPECIAL,\n ],\n"})}),"\n",(0,t.jsxs)(n.p,{children:["All rules defined in password column will be also delivered to ",(0,t.jsx)(n.a,{href:"/docs/tutorial/Plugins/email-password-reset",children:"password reset plugin"})," if you are using it to ensure that password reset will also respect same rules."]}),"\n",(0,t.jsx)(n.h2,{id:"trusting-client-ip-addresses",children:"Trusting client IP addresses"}),"\n",(0,t.jsxs)(n.p,{children:["Adminforth provides ",(0,t.jsx)(n.code,{children:"admin.auth.getClientIp(headers)"})," function to get client IP address."]}),"\n",(0,t.jsx)(n.p,{children:"This function is used for:"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsx)(n.li,{children:"Rate limiting in some standard plugins like those who are using OpenAI to protect against abuse"}),"\n",(0,t.jsx)(n.li,{children:"Securely logging user actions e.g. in Audit Log plugin"}),"\n",(0,t.jsx)(n.li,{children:"Can be used by your own code e.g. hooks to write first login/last login IP address to db"}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["By default it reads ",(0,t.jsx)(n.code,{children:"X-Forwarded-For"})," header from request headers to determine client IP address.\nAdminForth does not understand whether this header can be trusted or no. In some cases it might be spoofed by client like this:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{children:'curl -H "X-Forwarded-For: <made-out IP>" http://your-server/api...\n'})}),"\n",(0,t.jsx)(n.p,{children:"So you should take additional care to make sure that this header is not spoofed."}),"\n",(0,t.jsx)(n.h3,{id:"using-proxing-cdns",children:"Using proxing CDNs"}),"\n",(0,t.jsx)(n.p,{children:"If you are using proxying CDN like Cloudflare before your app which is probably best approach for security and performance, you should 3 things:"}),"\n",(0,t.jsxs)(n.ol,{children:["\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsx)(n.p,{children:"Make sure you use orange cloud in Cloudflare on domain serving app to proxy all traffic through Cloudflare."}),"\n"]}),"\n",(0,t.jsxs)(n.li,{children:["\n",(0,t.jsxs)(n.p,{children:["Set config auth.clientIpHeader to the header that CDN uses to pass client IP address.\nFor Cloudflare it is ",(0,t.jsx)(n.code,{children:"CF-Connecting-IP"}),":"]}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-ts",metastring:"./index.ts",children:"new AdminForth({\n ...\n auth: {\n clientIpHeader: 'CF-Connecting-IP'\n }\n}\n"})}),"\n",(0,t.jsxs)(n.ol,{start:"3",children:["\n",(0,t.jsxs)(n.li,{children:["Make sure that your server is only accepting requests from CDN IP addresses (or ranges). For Cloudflare this info is here: ",(0,t.jsx)(n.a,{href:"https://www.cloudflare.com/ips/",children:"https://www.cloudflare.com/ips/"})," . You can set it in firewall e.g. in AWS security group, or in software firewall like UFW. There is even a script which you can use in cron ",(0,t.jsx)(n.a,{href:"https://github.com/Paul-Reed/cloudflare-ufw",children:"https://github.com/Paul-Reed/cloudflare-ufw"})," (just in case if list of IPs will change in future)."]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"using-reverse-proxy",children:"Using reverse proxy"}),"\n",(0,t.jsxs)(n.p,{children:["If first client-facing point is reverse proxy like Nginx, or Traefik and there is no more proxies like CDN behind it, you should take care of reliably setting ",(0,t.jsx)(n.code,{children:"X-Forwarded-For"})," header to the client IP address.\nMain trick here is to make sure that proxy strips any ",(0,t.jsx)(n.code,{children:"X-Forwarded-For"})," headers that are already present in the requeqst and hardly overwrites it with client IP address coming from TCP connection (it can't be spoofed by client)."]}),"\n",(0,t.jsxs)(n.p,{children:["In Traefik to set ",(0,t.jsx)(n.code,{children:"X-Forwarded-For"})," header you should set ",(0,t.jsx)(n.code,{children:"forwardedHeaders"})," in traefik config:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-yaml",children:'\n adminforth:\n build: ./app\n environment:\n - NODE_ENV=production\n - ADMINFORTH_SECRET=!CHANGEME! # \u261d\ufe0f replace with your secret\n ...\n labels:\n - "traefik.http.middlewares.sanitize-headers.headers.customRequestHeaders.X-Forwarded-For=$remote_addr"\n # enable middleware\n - "traefik.http.routers.adminforth.middlewares=sanitize-headers"\n'})}),"\n",(0,t.jsxs)(n.p,{children:["For nginx you should set ",(0,t.jsx)(n.code,{children:"proxy_set_header X-Forwarded-For $remote_addr;"})," in your nginx config:"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-nginx",children:"server {\n ...\n location / {\n proxy_pass http://localhost:3000;\n proxy_set_header X-Forwarded-For $remote_addr;\n }\n}\n"})})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(c,{...e})}):c(e)}},8453:(e,n,s)=>{s.d(n,{R:()=>o,x:()=>a});var r=s(6540);const t={},i=r.createContext(t);function o(e){const n=r.useContext(i);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:o(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]);