ある日のこと
僕の運営している『ブラウザで遊べる絵チャット(令和最新版)』に、このような投稿がされてしまいました。
正直こんな荒らしは毎日のようにあるのですが、普段であればすぐに他のユーザーによって通報され、対処されます。
しかし、このユーザーは、何故か他ユーザーに通報されることなく、荒らし続けることができていました。
スクショをよく見ていただきたいのですが、通常であれば、「善良ユーザーA」のように発言の横にユーザーが名が表示され、そのユーザー名をクリックすることでプロフィールを表示することができるようになっています。
しかし、荒らしているユーザーには、なんと名前がないのです。
名前がないと、ユーザー名を押下することができず、プロフィールも表示できません。
違反行為の通報は、プロフィールから行うようになっているため、このユーザーは他ユーザーから通報されることなく、好きなだけおちんちん祭りを続けることができてしまったのです。
バリデーションちゃんとしてたのに!
ユーザー名には、空文字を設定できないように、以下のようなバリデーションを行っていました。
const submit = (name) => {
const trimedName = name.trim()
if (!trimedName) return alert('名前は必須です')
// ..
}
submit('') // 弾かれる
submit(' ') // 空白文字だけの場合も弾かれる
クライアント側だけでバリデーションしていたってオチでもありません。
サーバーサイドでも同様のバリデーションを行っていました。
では何故ユーザー名の無いユーザーが現れてしまったかと言うと、、
Unicodeには描画されない文字が多数存在する
以下の例をご覧ください。
const submit = (name) => {
const trimedName = name.trim()
if (!trimedName) return alert('名前は必須です')
// ..
}
submit('') // 弾かれない(!?)
一見、空文字を渡しているので、先ほど同様alertが出そうですが、上記のソースコードのsubmitは弾かれず、alertが表示されません。
実は、submit('')
のクォート内には、表示されない文字が含まれています。
このような目に見えない文字は、見えないだけで存在はしているので、Booleanに変換すればtrueになってしまいます。
trimでも取り除けませんし、文字数を数えてもカウントされてしまいます。
// 普通のあいうえお
console.log('あいうえお'.length) // -> 5
// あいうえおの間に目に見えない文字が含まれている
console.log('あいうえお'.length) // -> 9
冒頭の荒らしユーザーは、このような文字をユーザー名に用いていたのです。
対処方法
調べた限り、このような表示されない文字はたくさんあるようで、それらを簡単に検出する方法は見つけられませんでした。
とりあえず僕のサイトに関しては、何かしら有効な文字さえ含まれていればいいので、見えない文字を見つけるのではなく、見える文字が1文字以上含まれていることをバリデーションすることにしました。
if (!name.match(/[a-zA-Zぁ-んァ-ヶア-ン゙゚一-龠]/)) return false
追記: Unicode property escapesでより賢くできるようです。
目に見えない文字が生むその他の問題
他にも以下のような悪用が行えてしまうようです。
例えばQiitaでも…
Qiitaの記事はタイトル未入力では投稿できませんし、半角/全角スペースを使ってごまかすこともできませんが、さきほどの目に見えない文字を用いることで、Qiitaの記事でもタイトルの無い記事を投稿することができました。
同様のことができるサイトが他にもあると思いますが、これを悪用した不正やイタズラはおやめください
おわり
Unicodeについて詳しくありませんでしたが、こういう特殊な文字もあるんだなーと頭に留めておこうと思いました。