Qiita Careers powered by IndeedPR

求人サイト「Qiita Careers powered by Indeed」では、エンジニアのあなたにマッチした求人が見つかります。

354
190

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

目に見えない文字を悪用してサイトを好き放題荒らされた話

354
Last updated at Posted at 2022-12-02

ある日のこと

僕の運営している『ブラウザで遊べる絵チャット(令和最新版)』に、このような投稿がされてしまいました。

Screenshot from 2022-11-29 21-31-27.png

正直こんな荒らしは毎日のようにあるのですが、普段であればすぐに他のユーザーによって通報され、対処されます。

しかし、このユーザーは、何故か他ユーザーに通報されることなく、荒らし続けることができていました。

スクショをよく見ていただきたいのですが、通常であれば、「善良ユーザー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の記事でもタイトルの無い記事を投稿することができました。

Screenshot from 2022-11-29 19-08-43.png

同様のことができるサイトが他にもあると思いますが、これを悪用した不正やイタズラはおやめください

おわり

Unicodeについて詳しくありませんでしたが、こういう特殊な文字もあるんだなーと頭に留めておこうと思いました。


354
190
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

@laineus's pickup articles

laineus

@laineus

エンジニアリングマネージャー兼インディーゲーム開発者です。Webデザイナー出身でUI/UXに関心があります。
linkbal
街コンポータルサイト「machicon JAPAN」やマッチングサービス「CoupLink」などを開発・運営しています。

Linked from these articles

Comments

ripple_naip
@ripple_naip

Unicode property escapesでマッチさせる方法があるみたいです

制御文字とかマッチ

>>/\p{C}/gu.test('\u202e')
true

>>/\p{C}/gu.test('\t')
true 

一つでも表示できる文字があればいいならこんなのでいいかもしれません

>> /\p{L}|\p{M}|\p{N}|\p{P}|\p{S}/gu.test(' \u202e ') 
false 


>> /\p{L}|\p{M}|\p{N}|\p{P}|\p{S}/gu.test(' \u202e 😇')
true

10
laineus
@laineus
(Edited)

@ripple_naip ありがとうございます!この方が便利そうですね!


いくつか試していたところ、Markのカテゴリに属するU+E016D等もゼロ幅のようなので、\p{M}もブロックする必要があるようです。

コピペ用
󠅭
2
vram
@vram

自身で説明している文章中にもう答えが出ているような気がしますが
どこかに描画した幅が一定ピクセル以内だったら弾くとか方法がありそうですね

sample.js
const cv = document.createElement('canvas').getContext('2d');
console.log(cv.measureText('あいうえお').width);
2
Sunasuna24
@Sunasuna24

僕も似たようなことでWebサイトを止めたことがあるので、啓蒙活動としてこの記事はありがたいです、、、泣
※悪用絶対厳禁!!!

2

Let's comment your feelings that are more than good

Being held Article posting campaign

354
190

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Login to continue?

Login or Sign up with social account

Login or Sign up with your email address