ルート権限でプログラムでファイルに書き込むための最も安全な方法は何ですか?


35

巨大なアプリケーションは、ある特定の時点で、ルート権限を必要とするファイルへの少数の書き込みを実行する必要があります。実際にはファイルではなく、ファイルとしてLinuxに公開されるハードウェアインターフェイスです。

アプリケーション全体にルート権限を付与しないようにするために、重要なタスクを実行するbashスクリプトを作成しました。たとえば、次のスクリプトは、出力としてハードウェアインターフェイスのポート17を有効にします。

echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction

しかし、suid私のシステムのbashスクリプトでは無効になっているため、これを達成するための最良の方法は何でしょうか。

  1. ここに提示されいる回避策を使用してください

  2. sudoメインアプリケーションからを使用してスクリプトを呼び出し、それに応じてsudoersリストを編集して、スクリプトの呼び出し時にパスワードを要求しないようにします。にsudo権限を付与するのは少し不快ですecho

  3. でCプログラムを作成しfprintf、suid rootに設定します。文字列とファイル名をハードコーディングし、rootのみが編集できることを確認してください。または、テキストファイルから文字列を読み取り、同様に誰もファイルを編集できないようにします。

  4. 私が思いつかなかった他の解決策は、上記のものよりも安全または簡単ですか?


10
root特権でプログラムを起動し、ファイルを開き、特権をドロップしませんか?これが、すべてのWebサーバーなどがソケットに対して行う方法です。事実上、rootとして実行しているわけではなく、実行するヘルパーも必要ありません。
デイモン

回答:


33

sudoアクセスする必要はありませんecho。実際、これは無意味です。たとえば、sudo echo foo > barでは、リダイレクトはルートとしてではなく、元のユーザーとして行われます。

小さなスクリプトを呼び出すsudoことができ、NOPASSWD:それへのアクセスを必要とするユーザー(複数可)のみでそのスクリプト(および他の同様のスクリプト)へのアクセスを。

これは常にを使用するのに最適で最も安全な方法sudoです。ルート権限が必要な少数のコマンドを独自の個別のスクリプトに分離し、信頼されていないユーザーまたは部分的に信頼されているユーザーがそのスクリプトをルートとしてのみ実行できるようにします。

小さなsudo-ableスクリプトは、ユーザーからの引数(または入力)を受け取らない(つまり、呼び出す他のプログラムにはハードコードされたオプションと引数が必要です)か、必要な引数/入力を非常に慎重に検証する必要がありますユーザーから受け入れます。

検証には偏執的である-除外する「既知の悪い」ものを探し、「既知の良い」もののみを許可し、不一致やエラー、またはリモートで疑わしいものでも中止します。

検証は、スクリプトのできるだけ早い段階で行う必要があります(ルートとして何か他のことを行う前に行うことが望ましい)。


私は本当に私が最初にこの答えを書いたとき、これを言及している必要がありますが、あなたのスクリプトはシェルスクリプトであれば、それはしなければならない適切に引用するすべての変数を。でユーザによって供給される入力含む変数引用するように、特に注意してください任意の方法を、いくつかの変数が安全であると仮定しないでください、それら全てを引用

それは潜在的に(例えば、ユーザによって制御された環境変数が含ま"$PATH""$HOME""$USER"などの間違いを含む"$QUERY_STRING""HTTP_USER_AGENT"などのCGIスクリプトでは)。実際、それらすべてを引用してください。複数の引数を使用してコマンドラインを作成する必要がある場合は、配列を使用して引数リストを作成し、その引用符を付け"${myarray[@]}"ます。

まだ十分に頻繁に「それらを引用する」と言ったことがありますか それを覚えて。やる


18
スクリプト自体は、アクセス権500でルートが所有する必要があることを忘れてい
ワイルドカード

13
少なくとも、書き込み許可を削除してください。それが本当に私のポイントでした。残りはちょうど強化されています。
ワイルドカード

10
思慮深く書かれたCプログラムは、シェルスクリプトよりも攻撃対象が小さくなります。
user253751

7
@immibis、おそらく。ただし、シェルスクリプトよりも記述およびデバッグに時間がかかります。また、Cコンパイラーが必要です(一部の実動サーバーでは、攻撃者によるエクスプロイトのコンパイルを困難にすることにより、セキュリティリスクを軽減するために禁止されています)。また、初心者から中級レベルのシステム管理者またはプログラマーによって作成されたシェルスクリプトは、同様のスキルを持つ人によって作成されたCプログラムよりも悪用される可能性が低くなります(特にユーザー提供データを受け入れて検証する必要がある場合)。
cas

3
@JonasWielicki-私たちは今や事実ではなく、意見の領域に本当に真剣に取り組んでいると思います。シェルまたはC(またはperlまたはpythonまたはawkなど)に対して有効な引数を作成して、多かれ少なかれ悪用可能なミスを起こしやすくすることができます。本当に、それは主にプログラマーのスキルと細部への注意(そして疲れ、速攻、注意など)に依存するときです。ただし、低レベルの言語では、高レベルの言語ではるかに少ないコード行で実行できることを実現するためにより多くのコードを記述する必要があります。間違いを犯す。
cas

16

gpioファイルの所有者を確認します。

ls -l /sys/class/gpio/

ほとんどの場合、グループによって所有されていることがわかりますgpio

-rwxrwx--- 1 root     gpio     4096 Mar  8 10:50 export
...

その場合、ユーザーをgpioグループに追加するだけで、sudoを使用せずにアクセスを許可できます。

sudo usermod -aG gpio myusername

変更を有効にするには、ログアウトしてから再度ログインする必要があります。


機能しません。実際、すべてのグループの所有者/sys/class/gpio/はgpioですが、そのグループに自分を追加した後でも、そこに何かを書き込もうとすると「許可が拒否されます」。
-vsz

1
問題は、ファイル/sys/class/gpio/が実際に/sys/devices/platform/soc/<some temporary name>/gpioは所有者とグループの両方がルートである場所への単なるシンボリックリンクであるということです。
-vsz

4
@vsz試しましたchgrp gpio /sys/devices/platform/soc/*/gpioか?おそらくそのようなものをスタートアップファイルに入れることができます。
jpa

はい、しかしそれはそれほど単純ではありません。それらは常に異なる方法で生成されるため、次のようなものを使用する必要がありましたchgrp gpio `readlink -f /sys/class/gpio/gpio18`/*
-vsz

7

このための1つのソリューション(特にLinuxデスクトップで使用されますが、他の場合にも適用可能)は、D-Busを使用して、rootおよびpolkitとして実行されている小さなサービスをアクティブ化して承認を行うことです。これは基本的に、polkitが設計されたものです。導入ドキュメントから:

polkitは、非特権プログラム(「CLIENTS」)にサービスを提供する特権プログラム(「MECHANISMS」)が使用することを目的とした認証APIを提供します。システムアーキテクチャと全体像については、polkitのマニュアルページを参照してください。

ヘルパープログラムを実行するのではなく、大きな特権のないプログラムはバス上でリクエストを送信します。ヘルパーは、システムの起動時に起動されたデーモンとして実行されているか、または必要に応じてsystemdでアクティブ化されている可能性があります。次に、ヘルパーはpolkitを使用して、リクエストが許可された場所からのものであることを確認します。(または、この場合、それがやり過ぎのように感じる場合は、他のハードコードされた認証/承認メカニズムを使用できます。)

D-Busを介した通信に関する優れた基本的な記事を見つけましたが、テストしていませんが、これはpolkitをmixに追加する基本的な例のようです

このアプローチでは、setuidとしてマークする必要はありません。


5

これを行う1つの方法は、Cで記述されたsetuid-rootプログラムを作成することです。あなたの場合、ユーザー入力を見る必要はまったくありません。

#include <unistd.h>
#include <string.h>
#include <stdio.h>  // for perror(3)
// #include ...  more stuff for open(2)

static void write_str_to_file(const char*fn, const char*str) {
    int fd = open(fn, O_WRONLY)
    if (-1 == fd) {
        perror("opening device file");  // make this a CPP macro instead of function so you can use string concat to get the filename into the error msg
        exit(1);
    }
    int err = write(fd, str, strlen(str));
    // ... error check
    err = close(fd);
    // ... error check
}

int main(int argc, char**argv) {
    write_string_to_file("/sys/class/gpio/export", "17");
    write_string_to_file("/sys/class/gpio/gpio17/direction", "out");
    return 0;
}

環境変数などを使用してこれを破壊する方法はありません。実行するのは、いくつかのシステムコールを行うだけだからです。

欠点:すべてのシステムコールの戻り値を確認する必要があります。

利点:エラーチェックは非常に簡単です。エラーが発生した場合は、ただperror救済してください:ゼロ以外のステータスで終了します。エラーがある場合は、で調査してくださいstrace。本当に素晴らしいエラーメッセージを出すために、このプログラム自体は必要ありません。


2
2番目のファイルが存在しないように保護するために、何かを書く前に両方のファイルを開くように誘惑されるかもしれません。そして、私はこのプログラムを実行するかもしれないsudoので、それ自体はsetuidである必要はありません。ちなみに、それはの<fcntl.h>ためopen()です。
ジョナサンレフラー

3

sudoのエコーの代わりにティーを祝福することは、ルートのパーマを制限する必要がある状況にアプローチする一般的な方法の1つです。/ dev / nullへのリダイレクトは、出力の漏れを止めることです-teeはあなたが望むことをします。

echo "17" | sudo tee /sys/class/gpio/export > /dev/null
echo "out" | sudo tee /sys/class/gpio/gpio17/direction > /dev/null

5
での実行を許可teeするとsudo、任意のファイルを上書きまたは追加できます(たとえば、/etc/passwd新しいuid = 0アカウントを追加するだけです)。すべてのコマンドの実行を許可することもできますsudo(BTW /etc/sudoersは「任意のファイル」のセットに属しているため、上書きできますsudo tee
cas

2
どうやら、この回答の別の質問でtee説明されているように、書き込み可能なファイルを制限できます。一部の人々は使用された元の構文に問題があり、その問題の修正を提案するため、コメントも必ず読んでください。
アレックス

1
@alex、はい、それはsudoのどのコマンドでも可能です-許可される引数を制限します。を使用teeして多数のファイルを操作できるようにする場合、設定は非常に長く複雑になる可能性がありますsudo
cas

0

目的のタスクを実行するスクリプトを作成できます。次に、OpenSSHで使用されるキーを指定することによってのみログインできる新しいユーザーアカウントを作成します。

注:キーファイルがある場合は、誰でもそのスクリプトを実行できます。そのため、タスクの実行を禁止する人がOpenSSHキーファイルを読み取れないようにしてください。

OpenSSH構成(authorized_keysファイル内)で、キーを指定する前に、この「例」のテキストで説明されているように、コマンド(スペースが続く)を指定します。

command="myscript" keydata optionalComment

この構成オプションは、そのOpenSSHキーを特定のコマンドの実行のみに制限できます。これでsudoがアクセス許可を付与しましたが、OpenSSH構成は、ユーザーが実行できることを制限/制限するために実際に使用されているソリューションの一部であるため、ユーザーは他のコマンドを実行しません。また、これは「sudo」構成ファイルほど複雑ではないため、OpenBSDを使用する場合、またはOpenBSDの新しい「doas」(「do as」)がより一般的になり始めた場合(使用するオペレーティングシステムの将来のバージョンで) 、sudo構成が非常に複雑であることに挑戦する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.