From 0ef83441788ae041097343a384d571d3a6e19a43 Mon Sep 17 00:00:00 2001 From: Eugene Burkov Date: Thu, 10 Feb 2022 18:30:41 +0300 Subject: [PATCH] Pull request: 3503 password policy Merge in DNS/adguard-home from 3503-password-policy to master Closes #3503. Squashed commit of the following: commit 1f03cd9ef6e76a691ae383d6ba4b7f851eabddb8 Author: Eugene Burkov Date: Thu Feb 10 18:24:59 2022 +0300 client: imp msg commit e164ae2544284cf9a1d3333c50b68e361f78ce2a Merge: b7efd764 f53f48cc Author: Eugene Burkov Date: Thu Feb 10 16:52:01 2022 +0300 Merge branch 'master' into 3503-password-policy commit b7efd7640ec0fa3deac5290f8306ce5142428718 Author: Ildar Kamalov Date: Thu Feb 10 16:17:59 2022 +0300 client: remove empty line commit f19aba6cb579d2c4681675c881000c8f16257ab9 Author: Ildar Kamalov Date: Thu Feb 10 16:09:14 2022 +0300 client: validate password length commit a6943c94483306ecfc0d1431d576d42053823b61 Author: Eugene Burkov Date: Fri Feb 4 18:57:02 2022 +0300 all: fix docs again commit 9346bb6c393af0799a79b228285acdd8f8799b83 Author: Eugene Burkov Date: Fri Feb 4 18:54:15 2022 +0300 openapi: fix docs commit a8016443237c130f69108970ddfc77ef71126be6 Author: Eugene Burkov Date: Fri Feb 4 18:25:55 2022 +0300 all: validate passwd runes count --- CHANGELOG.md | 2 + client/package-lock.json | 114 +++++++++++++++++++++++-------- client/package.json | 1 + client/src/__locales/en.json | 3 +- client/src/helpers/constants.js | 2 + client/src/helpers/validators.js | 14 +++- client/src/install/Setup/Auth.js | 3 +- internal/home/controlinstall.go | 16 +++++ openapi/CHANGELOG.md | 6 ++ openapi/openapi.yaml | 3 + 10 files changed, 133 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b14fa13..54c916d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ In this release, the schema version has changed from 12 to 13. ### Security +- Enforced password strength policy ([3503]). - Weaker cipher suites that use the CBC (cipher block chaining) mode of operation have been disabled ([#2993]). @@ -79,6 +80,7 @@ In this release, the schema version has changed from 12 to 13. [#2993]: https://github.com/AdguardTeam/AdGuardHome/issues/2993 [#3057]: https://github.com/AdguardTeam/AdGuardHome/issues/3057 [#3367]: https://github.com/AdguardTeam/AdGuardHome/issues/3367 +[#3503]: https://github.com/AdguardTeam/AdGuardHome/issues/3503 [#4216]: https://github.com/AdguardTeam/AdGuardHome/issues/4216 [#4221]: https://github.com/AdguardTeam/AdGuardHome/issues/4221 [#4238]: https://github.com/AdguardTeam/AdGuardHome/issues/4238 diff --git a/client/package-lock.json b/client/package-lock.json index 09746154..ba1d3772 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1700,6 +1700,12 @@ "v8-to-istanbul": "^4.1.3" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -1720,6 +1726,12 @@ "supports-color": "^7.1.0" } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1747,6 +1759,25 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -3959,10 +3990,9 @@ "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" }, "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.0.tgz", + "integrity": "sha512-oGu2QekBMXgyQNWPDRQ001bjvDnZe4/zBTz37TMbiKz1NbNiyiH5hRkobe7npRN6GfbGbxMYFck/vQ1r9c1VMA==" }, "character-entities": { "version": "1.2.4", @@ -10037,6 +10067,12 @@ "string-length": "^4.0.1" }, "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", @@ -10057,6 +10093,12 @@ "supports-color": "^7.1.0" } }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -10078,6 +10120,25 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -14193,38 +14254,26 @@ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", + "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" + "char-regex": "^2.0.0", + "strip-ansi": "^7.0.1" }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^6.0.1" } } } @@ -14297,6 +14346,15 @@ "define-properties": "^1.1.3" } }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "stringify-entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.0.1.tgz", diff --git a/client/package.json b/client/package.json index aa96f271..22b83010 100644 --- a/client/package.json +++ b/client/package.json @@ -42,6 +42,7 @@ "redux-actions": "^2.6.5", "redux-form": "^8.3.5", "redux-thunk": "^2.3.0", + "string-length": "^5.0.1", "url-polyfill": "^1.1.9" }, "devDependencies": { diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 07021b59..9917ef2e 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -627,5 +627,6 @@ "use_saved_key": "Use the previously saved key", "parental_control": "Parental Control", "safe_browsing": "Safe Browsing", - "served_from_cache": "{{value}} (served from cache)" + "served_from_cache": "{{value}} (served from cache)", + "form_error_password_length": "Password must be at least {{value}} characters long" } diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js index d4477b72..197cabba 100644 --- a/client/src/helpers/constants.js +++ b/client/src/helpers/constants.js @@ -26,6 +26,8 @@ export const R_WIN_ABSOLUTE_PATH = /^([a-zA-Z]:)?(\\|\/)(?:[^\\/:*?"<>|\x00]+\\) export const R_CLIENT_ID = /^[a-z0-9-]{1,63}$/; +export const MIN_PASSWORD_LENGTH = 8; + export const HTML_PAGES = { INSTALL: '/install.html', LOGIN: '/login.html', diff --git a/client/src/helpers/validators.js b/client/src/helpers/validators.js index 2ebf6a30..862a2ca4 100644 --- a/client/src/helpers/validators.js +++ b/client/src/helpers/validators.js @@ -1,4 +1,5 @@ import i18next from 'i18next'; +import stringLength from 'string-length'; import { MAX_PORT, @@ -13,6 +14,7 @@ import { UNSAFE_PORTS, R_CLIENT_ID, R_DOMAIN, + MIN_PASSWORD_LENGTH, } from './constants'; import { ip4ToInt, isValidAbsolutePath } from './form'; import { isIpInCidr, parseSubnetMask } from './helpers'; @@ -320,10 +322,20 @@ export const validatePath = (value) => { * @param cidr {string} * @returns {Function} */ - export const validateIpv4InCidr = (valueIp, allValues) => { if (!isIpInCidr(valueIp, allValues.cidr)) { return i18next.t('form_error_subnet', { ip: valueIp, cidr: allValues.cidr }); } return undefined; }; + +/** + * @param value {string} + * @returns {Function} + */ +export const validatePasswordLength = (value) => { + if (value && stringLength(value) < MIN_PASSWORD_LENGTH) { + return i18next.t('form_error_password_length', { value: MIN_PASSWORD_LENGTH }); + } + return undefined; +}; diff --git a/client/src/install/Setup/Auth.js b/client/src/install/Setup/Auth.js index b4184191..c2314aa5 100644 --- a/client/src/install/Setup/Auth.js +++ b/client/src/install/Setup/Auth.js @@ -8,6 +8,7 @@ import i18n from '../../i18n'; import Controls from './Controls'; import { renderInputField } from '../../helpers/form'; import { FORM_NAME } from '../../helpers/constants'; +import { validatePasswordLength } from '../../helpers/validators'; const required = (value) => { if (value || value === 0) { @@ -67,7 +68,7 @@ const Auth = (props) => { type="password" className="form-control" placeholder={ t('install_auth_password_enter') } - validate={[required]} + validate={[required, validatePasswordLength]} autoComplete="new-password" /> diff --git a/internal/home/controlinstall.go b/internal/home/controlinstall.go index 82598078..98cbf31a 100644 --- a/internal/home/controlinstall.go +++ b/internal/home/controlinstall.go @@ -13,6 +13,7 @@ import ( "runtime" "strings" "time" + "unicode/utf8" "github.com/AdguardTeam/AdGuardHome/internal/aghalg" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" @@ -359,6 +360,9 @@ func shutdownSrv(ctx context.Context, srv *http.Server) { } } +// PasswordMinRunes is the minimum length of user's password in runes. +const PasswordMinRunes = 8 + // Apply new configuration, start DNS server, restart Web server func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) { req, restartHTTP, err := decodeApplyConfigReq(r.Body) @@ -368,6 +372,18 @@ func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) { return } + if utf8.RuneCountInString(req.Password) < PasswordMinRunes { + aghhttp.Error( + r, + w, + http.StatusUnprocessableEntity, + "password must be at least %d symbols long", + PasswordMinRunes, + ) + + return + } + err = aghnet.CheckPort("udp", req.DNS.IP, req.DNS.Port) if err != nil { aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) diff --git a/openapi/CHANGELOG.md b/openapi/CHANGELOG.md index ef32f6ea..0762fdf2 100644 --- a/openapi/CHANGELOG.md +++ b/openapi/CHANGELOG.md @@ -4,6 +4,12 @@ ## v0.107.3: API changes +### The new possible status code in `/install/configure` response. + +* The new status code `422 Unprocessable Entity` in the response for + `POST /install/configure` which means that the specified password does not + meet the strength requirements. + ### The new field `"version"` in `AddressesInfo` * The new field `"version"` in `GET /install/get_addresses` is the version of diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 30d25b44..85c372a3 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -1088,6 +1088,9 @@ 'description': > Failed to parse initial configuration or cannot listen to the specified addresses. + '422': + 'description': > + The specified password does not meet the strength requirements. '500': 'description': 'Cannot start the DNS server' '/login':