Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Consider more user inputs when calculating zxcvbn score #11180

Merged
merged 2 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/components/views/auth/PassphraseField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
minScore: 0 | 1 | 2 | 3 | 4;
value: string;
fieldRef?: RefCallback<Field> | RefObject<Field>;
// Additional strings such as a username used to catch bad passwords
userInputs?: string[];

label: string;
labelEnterPassword: string;
Expand All @@ -57,7 +59,7 @@ class PassphraseField extends PureComponent<IProps> {
deriveData: async ({ value }): Promise<zxcvbn.ZXCVBNResult | null> => {
if (!value) return null;
const { scorePassword } = await import("../../../utils/PasswordScorer");
return scorePassword(MatrixClientPeg.get(), value);
return scorePassword(MatrixClientPeg.get(), value, this.props.userInputs);
},
rules: [
{
Expand Down
1 change: 1 addition & 0 deletions src/components/views/auth/RegistrationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
value={this.state.password}
onChange={this.onPasswordChange}
onValidate={this.onPasswordValidate}
userInputs={[this.state.username]}
/>
);
}
Expand Down
23 changes: 18 additions & 5 deletions src/utils/PasswordScorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import zxcvbn, { ZXCVBNFeedbackWarning } from "zxcvbn";
import { MatrixClient } from "matrix-js-sdk/src/matrix";

import { _t, _td } from "../languageHandler";
import { MatrixClientPeg } from "../MatrixClientPeg";

const ZXCVBN_USER_INPUTS = ["riot", "matrix"];

Expand Down Expand Up @@ -59,20 +60,32 @@ _td("Short keyboard patterns are easy to guess");
*
* @param {string} password Password to score
* @param matrixClient the client of the logged in user, if any
* @param userInputs additional strings such as the user's name which should be considered a bad password component
* @returns {object} Score result with `score` and `feedback` properties
*/
export function scorePassword(matrixClient: MatrixClient | null, password: string): zxcvbn.ZXCVBNResult | null {
export function scorePassword(
matrixClient: MatrixClient | null,
password: string,
userInputs: string[] = [],
): zxcvbn.ZXCVBNResult | null {
if (password.length === 0) return null;

const userInputs = ZXCVBN_USER_INPUTS.slice();
const inputs = [...userInputs, ...ZXCVBN_USER_INPUTS];
if (matrixClient) {
userInputs.push(matrixClient.getUserIdLocalpart()!);
inputs.push(matrixClient.getUserIdLocalpart()!);
}

let zxcvbnResult = zxcvbn(password, userInputs);
try {
const domain = MatrixClientPeg.getHomeserverName();
inputs.push(domain);
} catch {
// This is fine
}

let zxcvbnResult = zxcvbn(password, inputs);
// Work around https://github.com/dropbox/zxcvbn/issues/216
if (password.includes(" ")) {
const resultNoSpaces = zxcvbn(password.replace(/ /g, ""), userInputs);
const resultNoSpaces = zxcvbn(password.replace(/ /g, ""), inputs);
if (resultNoSpaces.score < zxcvbnResult.score) zxcvbnResult = resultNoSpaces;
}

Expand Down