Мир стоит вверх ногами. Корпорация добра печется о нашей безопасности. И в то же время делает все, чтобы наши секреты стали достоянием гласности. Это я о политике гугл в плане сохранения логин-паролей в браузере, через него в аккаунте и о доступности всего нашего секретного добра всем, кто сможет подойти к нашим компьютерам.
Да, удобно. Но небезопасно. И временами очень вредно.
Запустил я очередной проект на работе. И самой большой проблемой по опыту старого проекта было то, что сотрудники вовсю пользовались автосохраненим паролей, поэтому под Васей мог сидеть Сергей и ни о какой нормальной аутентификации, а тем более разраничении прав доступа речи идти не могло.
Сначала двинулся в сторону того же гугла — как отключить или запретить автосохранение или автоподстановку сохраненного пароля? Старые рецепты с autocomplete="off"
и autocomplete="new-password"
уже не работают, последний работающий рецепт оказался прост — головную боль следует лечить усекновением болящей. То есть глобальным отключением автосохранения. Но это не вариант. Пусть юзеры дальше балуются своими паролями от почты, соцсетей и прочих радостей жизни, мне не жалко.
Далее последовал легкий инжиниринг. И вот какой результат — если на страничке есть input
type="password"
, то при появлении первого символа в адресной строке появляется ключик. И все. Что ты ни делай, какие html тэги ни прописывай — есть password
, значит, надо бежать и барину кланяться, предлагая все мыслимые и немыслимые блага от того, что барин позволит сохранить пароль. Ну и потом барин будет весьма доволен, что пароль не надо вспоминать, а просто его вставить.
Хмм... подумал я... И решил — раз у хрома такая нервная реакция на type="password"
, то надо просто от него отказаться и всех делов то!
Сказано — сделано. Изваял компонент, который незамысловато делает простейшую вещь — на экран выводит точечки, а в v-model передает то, что введено. Ну и плюсом отрабатывает клавишу Enter.
Конечно, не обошлось без ограничений. Но они очень малы и незначительны — нельзя курсор перемещать стрелками, только backspace. Но, учитывая, что под точками и так не видно, где и что надо подправить — вероятность испытать колоссальное неудобство от такого ограничения стремится к нулю. Не стал делать кнопочку с глазиком, чтобы посмотреть, что же я ввел, label
для input
оформил в стиле компонента vuetify, то есть текст-подсказка уползает вверх при фокусе и возвращается при утере фокуса при условии, что ничего не было введено.
Ну и использование такого компонента тоже очень простое — создать .vue файл, импортировать в проект и использовать в шаблоне.
Теперь, собственно, код.
Код под спойлером
<template>
<div class="logpass">
<input type="hidden" v-model="opass">
<label :for="inpid">Пароль</label>
<input style="width: 100%"
:id="inpid"
type="text"
v-model="ipass"
@input="input"
@keydown="keyDown"
@focus="focus"
@blur="blur"
@keydown.enter="$emit('keydown_enter',$event)">
</div>
</template>
<script>
export default {
name: "login-password",
data() {
return {
ipass:'', // input password
opass:'', // output password
}
},
computed: {
inpid(){
let length=7
let result
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let el=''
while(el!==null){ // void to duplicate id if node with same id exists
result=letters.charAt(Math.floor(Math.random() *
letters.length));
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() *
characters.length));
}
el=document.querySelector('#'+result)
}
return result;
},
},
methods: {
focus(event){
let el=document.querySelector(['label[for="'+event.target.id+'"]'])
if(el){
el.classList.add('label-active')
}
},
blur(event){
let el=document.querySelector(['label[for="'+event.target.id+'"]'])
if(el && this.opass==''){
el.classList.remove('label-active')
}
},
input(event){
if(this.opass.length<this.ipass.length){
let s = this.ipass.slice(-1)
this.opass=''.concat(this.opass,s)
s = ''
for(let i=0;i<this.ipass.length;i++)s=''.concat(s,'•')
this.ipass=s
this.$emit('input',this.opass)
}else{
if(this.opass.length>this.ipass.length){
this.opass=this.opass.slice(0,this.ipass.length)
this.$emit('input',this.opass)
}
}
},
keyDown(event){
if(event.key=='ArrowLeft'||event.key=='ArrowRight'||event.key=='ArrowUp'||event.key=='ArrowDown'){
event.preventDefault()
}
},
},
}
</script>
<style scoped lang="scss">
.logpass{
padding-top:14px;
padding-bottom: 12px;
display: flex;
& label{
padding: 5px;
position: absolute;
color: #999;
transition: .3s cubic-bezier(.25,.8,.5,1);
}
& .label-active{
font-size: 10px;
margin-top: -20px;
margin-left: -5px;
}
}
</style>
Как видим — достаточно простая логика — при вводе очередного символа он заменяется на точку и дублируется в переменную, переданную в v-model
. Именно запрет на перемещение курсора в поле ввода позволил реализацию такой логики.
Еще одно замечание — этот компонент не использует инициализацию значением, которое, возможно уже было в v-model
-переменной.
Пример использования:
// какой-нибудь наш модуль на vue, в котором необходим ввод пароля
<template>
<div>
логин: <input type="text" v-model="mylogin">
<login-password v-model="mypassword" @keydown_enter="makeLogin">
</login-password>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
mylogin: '',
mypassword:''
}
},
components: {
LoginPassword:()=>import('./login-password')
},
methods:{
makeLogn(){
.....
}
.......
Ну и напоследок — код свободный, берите, кто хотите, возможно, кому-то идея подойдет на React или Angular.
Комментарии (31)
KivApple
16.11.2021 13:59Очевидное техническое решение административных проблем.
Какими html тегами будете бороться с записью пароля на стикер на мониторе?
marsdenden Автор
16.11.2021 14:01ну как только всплывет косяк под учеткой одного, сделанный другим - сами будут думать.
KivApple
16.11.2021 14:35Так это решает и проблему запоминания пароля. Как только всплывёт косяк одного под учёткой другого, перестанут сохранять пароли на общих компьютерах (а на персональных сохранять в целом ок, если это не пульт управления ядерными боеголовками, но в высоко критичных областях обычно к паролю добавляют второй фактор типа биометрии, смарт-карт и т. п.).
marsdenden Автор
16.11.2021 15:21+1вот оно мне надо - бегать и искать эти косяки? Сиди потом, ковыряйся в логах, смотри на камеры, опознавай, кто подходил и что делал... Я сразу перекрыл этот путь. Зашли под твоим паролем - твои проблемы. Либо ты его раздавал, либо тупо вкладку не закрыл.
kipar
16.11.2021 14:02+4Я когда встал похожий вопрос нагуглил рецепт - добавлять к идентификатору поля случайную строку. Тогда сохранять он предлагает, но в следующий раз уже ее не подставит т.к. поле другое. Правда любой более-менее разбирающийся человек сможет вытащить его из браузера, но в моем случае это проблемой не было.
Но ваш метод явно лучше.
NikitchenkoSergey
16.11.2021 14:06+3Можно проще: сделать type=«text» и навесить стиль -webkit-text-security
developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-security
работает не везде, где не работает можно оставить type=«password», т.к. только хром так навязчиво с паролями работает. Ну и в интернете есть полифилы на шрифтах.marsdenden Автор
16.11.2021 14:23ну в основном хром, потом - это для внутреннего употребления и мне оказалось проще сделать так, чем возиться со стилями и определениями юзер-агента. И потом, нет гарантий, что в будущем хром не будет распознавать такую комбинацию типа и стиля
Ritan
16.11.2021 15:46+1Любая поломка UX - зло невозможное. Очень и очень надеюсь, что подобный мусор останется только там, где его и сваяли и не выползет никуда за пределы.
Но они очень малы и незначительны — нельзя курсор перемещать стрелками, только backspace.
А то каждый мнит, что ему решать что неудобство, а что - мелочь. Я, может, помню что у меня за пароль и знаю где опечатался. В конце-концов пропуск первого символа уж точно легко заметить и исправить
Если люди захотят, они и на рабочем столе файл passwords.txt оставят. И никакие подобные "умные" решения их не остановят
marsdenden Автор
16.11.2021 18:02-1зло - это когда тебе делают нечто, тебя не спросив. И не предоставив адекватных вариантов. Так что я считаю злом навязчивость корпорации. А если нужен гиковский функционал вслепую править вбиваемый пароль - велкам, код открыт, сделайте свой вариант.
ЗЫ. Кстати, да, спасибо за подсказку. Нужно еще home/end включить в блокировкуRitan
16.11.2021 20:50+3Щелчки мышью ещё запретите. А лучше сразу мышь отключайте на странице, чтобы тупой пользователь куда-нибудь не туда не ткнул в вашем поделии. И клавиатуру заодно, а то там можно ещё пяток способ переходить по тексту придумать.
aamonster
16.11.2021 22:20+1В хроме есть прекрасная кнопка – не запоминать пароли на этом сайте. Прямо в окошке "сохранить пароль?" (Кстати, это как раз вопрос. Точно "не спросив"?)
Getequ
16.11.2021 16:07+2Непонятно почему заминусили - проблема очень актуальная, особенно неприятно когда на сайте банка срабатывает автокомплит логина+пароля простым щелчком в поле логина не вводя ни одной буквы
dartraiden
16.11.2021 21:02+6Лично для меня актуальная иная проблема: я сам хочу решать, могу я вставить в поле скопированный из менеджера паролей пароль, а не терпеть навязчивость веб-мастера, который решил, что я должен свой 50-символьный пароль из букв, цифр и спецсимволов перепечатывать вручную.
aamonster
16.11.2021 21:39Вполне понятно: любой, кто старается сделать людям неудобно – враг, независимо от того, насколько благими намерениями он руководствуется.
Хотел бы решить проблему – применил бы решение, удобное для пользователей. Благо, они давно придуманы. Для внутреннего применения – например, смарткарты, раз уж есть необходимость защищаться от коллег. Причём достаточно блокировать компьютер – чтобы пользователю не приходилось запоминать пароли ко всем вашим сайтикам, а достаточно было запомнить пароль от компа.
shifttstas
17.11.2021 10:16+1Так это проблема вашей платформы или браузера, на маке надо Touch ID пройти что бы сработал автокомплит, на iPhone - Face ID.
Очевидным решением было бы разделение сайтов по уровням конфиденциальности и там где риски высокие - требовать что бы браузер проводил сверку через биометрию.
PS С друной стороны - зачем всё это если есть 2FA/OTP ?
marsdenden Автор
17.11.2021 13:24+1есть жизнь за мкадом и без айфонов.
shifttstas
17.11.2021 15:28+1При отсутствии сенсоров бимотреии пароли можно хранить в менеджере и вставлять только при вводе мастер-пароля
marsdenden Автор
17.11.2021 15:46бегать и всем мастер-пароли устанавливать? а есть и такие, до которых 200км бежать
shifttstas
17.11.2021 16:42200км до клавиатуры? мастер-пароль я имею ввиду в вашем менеджере паролей.
marsdenden Автор
17.11.2021 19:13вы не поняли тему. Это сделано для приложения, которое используется внутри компании. Есть сотрудники, которые сидят удаленно, есть по городу. Если по городу могу всеми управлять удаленно, то теми, кто на реальной удаленке управлять нет возможности. Поэтому мастер-пароль - это вообще не то. И мой менеджер паролей тоже не при чем.
shifttstas
18.11.2021 10:17Если мы говорим о Enterprise рынке - тогда ограничение на ввод и/или использование того или иного софта должно быть сделано на уровне устройства пользователя (корпоративного или BYOD) через MDM. Это даже еще проще - просто отключить автокомплит через групповые политики.
dartraiden
16.11.2021 21:08+9Немного букмарклетов, позволяющий бороться с навязчивостью создателей веб-ресурсов, считающих, что им виднее:
Разрешить операции копирования и вставки на странице:javascript:(function(w){var%20arr=['contextmenu','copy','cut','paste','mousedown','mouseup','beforeunload','beforeprint'];for(var%20i=0,x;x=arr[i];i++){if(w['on'+x])w['on'+x]=null;w.addEventListener(x,function(e){e.stopPropagation()},true)};for(var%20j=0,f;f=w.frames[j];j++){try{arguments.callee(f)}catch(e){}}})(window);
Сделать доступными заблокированные поля:javascript:(function(){function process(tag){var i,l,els=document.getElementsByTagName(tag);for(i=0,l=els.length;i<l;i++){els[i].readOnly = null;els[i].disabled = null;}}process('input');process('select');process('textarea');process('button');})()
Показать значения, скрытые за «звёздочками»:javascript:(function(){var%20s,F,j,f,i;%20s%20=%20"";%20F%20=%20document.forms;%20for(j=0;%20j<F.length;%20++j)%20{%20f%20=%20F[j];%20for%20(i=0;%20i<f.length;%20++i)%20{%20if%20(f[i].type.toLowerCase()%20==%20"password")%20s%20+=%20f[i].value%20+%20"\n";%20}%20}%20if%20(s)%20alert("Passwords%20in%20forms%20on%20this%20page:\n\n"%20+%20s);%20else%20alert("There%20are%20no%20passwords%20in%20forms%20on%20this%20page.");})();
Показать все скрытые поля:javascript:(function(){var%20i,f,j,e,div,label,ne;%20for(i=0;f=document.forms[i];++i)for(j=0;e=f[j];++j)if(e.type=="hidden"){%20D=document;%20function%20C(t){return%20D.createElement(t);}%20function%20A(a,b){a.appendChild(b);}%20div=C("div");%20label=C("label");%20A(div,%20label);%20A(label,%20D.createTextNode(e.name%20+%20":%20"));%20e.parentNode.insertBefore(div,%20e);%20e.parentNode.removeChild(e);%20ne=C("input");/*for%20ie*/%20ne.type="text";%20ne.value=e.value;%20A(label,%20ne);%20label.style.MozOpacity=".6";%20--j;/*for%20moz*/}})()
aamonster
16.11.2021 21:40И кого больше получилось в результате вашего хака – пользователей с паролем qwerty123 или пользователей со стикером на мониторе? ;-)
marsdenden Автор
17.11.2021 08:38ни одного в обоих категориях. Решение для внутреннего использования, в проде уже неделю, негативной реакции нет.
DarthVictor
17.11.2021 01:00+1Кастомным шрифтом, состоящим из одного символа на все буквы это делается. Для полей для кодов подтверждения, если их просят скрыть (я сам не люблю, когда скрывают, но это вопрос вкуса) это самое нормальное решение. Оно и кроссплатформено в отличие от --webkit-text-security. Хотя если FF не нужен, то последний короче.
Вопрос осмысленности запрета сохранения паролей я пожалуй обсуждать не буду.
Dzzzen
17.11.2021 20:21+2А не проще было через групповые политики отключить сохранение паролей в Chrome сразу во всей организации?
Ilusha
2FA: Google Authenticator.