लॉन्च से पहले SSH हेल्पर कैसे सत्यापित करें
जानें कि macOS पर SSH हेल्पर के बंडल पथ, हस्ताक्षर, मालिकाना हक, फ़ाइल पहचान और लॉन्च-समय कोड आवश्यकता को कैसे जाँचें।

स्थानीय गेटवे को अपने SSH हेल्पर को विशेष अधिकार वाला कोड मानना चाहिए, भले ही हेल्पर के पास कोई वॉल्ट न हो और वह कोई स्थिति सहेजकर न रखता हो। हेल्पर कमांड लेता है, फ़ाइल डिस्क्रिप्टर और वातावरण विरासत में पाता है, और वही प्रक्रिया बन सकता है जो दूरस्थ होस्ट से बात करती है। यदि कोई हमलावर उस executable को बदल सकता है, तो उसके ऊपर दी गई हर मंज़ूरी बेकार हो जाती है।
सुरक्षित डिज़ाइन में दो अलग काम हैं। प्रीफ्लाइट जाँच किसी अनुरोध के रनर तक पहुँचने से पहले टूटे या गलत तरीके से इंस्टॉल किए गए बंडल का पता लगाती है। लॉन्च-समय कोड आवश्यकता macOS से कहती है कि वास्तविक process image की signing identity गलत हो तो उसे चलने न दे। इन दोनों कामों को एक मानने पर वही पुराना अंतर बनता है जिसमें ऐप एक फ़ाइल सत्यापित करता है और दूसरी को चलाता है।
हेल्पर को चल रहे बंडल से तय करें
हेल्पर को उसी ऐप बंडल से तय करें जो सच में चल रहा है, कभी भी PATH, मौजूदा डायरेक्टरी, किसी preference या एजेंट से मिले पथ से नहीं। अपेक्षित स्थान build-time constant होना चाहिए। जो गेटवे sp-ssh खोजता है, वह executable को चुनने का अधिकार पहले ही अपने वातावरण को दे चुका है।
Apple nested code के मानक bundle स्थान बताता है, जिनमें Contents/MacOS और Contents/Helpers शामिल हैं। एक स्थान चुनें, वहाँ केवल code रखें, और हेल्पर कहीं और पहुँचे तो build को विफल करें। पहले हेल्पर पर हस्ताक्षर करें और सबसे अंत में बाहरी ऐप पर। इस क्रम से बाहरी हस्ताक्षर उसके nested code reference को seal कर सकता है।
Foundation auxiliary executable खोज सकता है, लेकिन सुरक्षा निर्णय को फिर भी main bundle से उसका सटीक संबंध जाँचना होगा। दोनों URL के symbolic link resolve करके उन्हें standardize करें, फिर string prefix के बजाय path component की तुलना करें। candidate.path.hasPrefix(bundle.path) जैसा परीक्षण /Applications/Good.app.backup जैसे पड़ोसी पथ स्वीकार करता है और case या normalization पर गलत हो सकता है। candidate को चल रहे bundle के भीतर मौजूद उसी एक अपेक्षित URL के बराबर होना चाहिए।
ऐप के पास कॉपी किए गए हेल्पर को fallback न बनाएँ। विकास के समय यह सुविधाजनक लगता है क्योंकि गलत packaging परेशान नहीं करती। उत्पादन में यह चुपचाप trust boundary को signed bundle content से हटाकर पास के pathname पर मौजूद किसी भी फ़ाइल तक ले जाता है। Development build में साफ build configuration रखें और packaging गलत हो तो स्पष्ट विफलता दें।
स्थान packaging का प्रमाण है, पहचान का नहीं। writable bundle के भीतर फ़ाइल बदल सकने वाला हमलावर वही पथ रख सकता है। इसलिए अगली जाँच खुले हुए object और उसके हस्ताक्षर को देखती है।
पहले खोलें, फिर फ़ाइल की पहचान जाँचें
candidate को O_NOFOLLOW के साथ खोलें, descriptor खुला रखें और उसी descriptor पर fstat चलाएँ। यह क्रम ज़रूरी है। lstat चलाकर नतीजा जाँचना और बाद में open करना दूसरी प्रक्रिया को इन कामों के बीच directory entry बदलने का मौका देता है।
Apple का Secure Coding Guide इसी कारण descriptor पर आधारित operation सुझाता है। वह खोलने के बाद type, UID, GID, mode और link count जाँचने को स्पष्ट रूप से कहता है। executable हेल्पर के लिए मेरी preflight जाँच इस तरह होती है:
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
int inspect_helper(const char *path, uid_t expected_uid, struct stat *snapshot) {
int fd = open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (fd < 0) return -1;
struct stat st;
if (fstat(fd, &st) != 0 ||
!S_ISREG(st.st_mode) ||
st.st_uid != expected_uid ||
(st.st_mode & (S_IWGRP | S_IWOTH)) != 0 ||
(st.st_mode & S_IXUSR) == 0 ||
st.st_nlink != 1) {
int saved = errno ? errno : EPERM;
close(fd);
errno = saved;
return -1;
}
*snapshot = st;
return fd;
}
expected_uid installation model से आता है, फ़ाइल से नहीं। system-managed installation में root ownership ज़रूरी हो सकता है। per-user app का उसी user के नाम होना सही हो सकता है। केवल इसलिए UID 0 की माँग न करें कि root भरोसेमंद दिखता है। Apple यह भी चेतावनी देता है कि पथ दूसरे mounted filesystem पर जा सकता है, जहाँ ownership अकेले उतना नहीं बताता जितना developer अक्सर समझते हैं।
Link count का नियम सोच-समझकर तय करें। सामान्य bundled executable का एक hard link होता है, इसलिए दूसरी संख्या अस्वीकार करना उचित है। यदि packaging system जानबूझकर hard link बनाता है, तो यह बात लिखें और अपेक्षित संख्या का परीक्षण करें। किसी build के चौंकाने पर जाँच हटा देने से उसी inode का एक दूसरा नाम बच जाता है, जिसके रास्ते उसे बदला जा सकता है।
लॉन्च पूरा होने तक descriptor और stat snapshot रखें। वे path-based spawn को atomic नहीं बनाते, लेकिन replacement पकड़ने, device और inode दर्ज करने और हमलावर के नियंत्रण वाला नाम दोबारा खोले बिना इनकार समझाने में मदद करते हैं।
पथ की हर writable directory जाँचें
सही helper mode भी उस helper को नहीं बचाता जिसकी parent directory का नाम बदला या content लिखा जा सकता हो। यदि हमलावर executable के bytes नहीं बदल सकता, तब भी वह उन bytes की ओर इशारा करने वाली directory entry बदल सकता है।
Directory descriptor के साथ helper के parent से verified bundle root तक चलें। हर component पर symbolic link अस्वीकार करें, पुष्टि करें कि वह directory है, उसका device और inode दर्ज करें, और installation model का ownership तथा write-permission नियम लगाएँ। Parent descriptor पकड़े हुए openat और fstatat का उपयोग बार-बार absolute string resolve करने से बेहतर है। traversal पहले से सत्यापित bundle root पर रुके, सिर्फ परिचित suffix वाले किसी पथ पर नहीं।
Per-user installation के बारे में साफ बोलना पड़ता है। यदि gateway चलाने वाला user writable app bundle का मालिक है, तो उसी user के रूप में चल रही दूसरी प्रक्रिया bundle file बदल सकती है। Mode और owner जाँच दूसरे account के लिए अनजाने exposure का पता लगा सकती है, लेकिन current account के पूरे compromise से नहीं बचाती। Code-signing identity फिर भी ज़रूरी है, क्योंकि attacker केवल ad hoc signature लगाकर आपकी designated requirement पूरी नहीं कर सकता।
यदि policy स्थानीय, single-volume bundle चाहती है तो अपेक्षित chain में device ID भी मिलाएँ। Apple बताता है कि pathname mount boundary पार कर सकता है। अनपेक्षित boundary अस्वीकार करना उपयोगी है, लेकिन इससे volume विश्वसनीय सिद्ध नहीं होता। यह केवल बताता है कि object packaging contract की जगह पर नहीं है।
Directory जाँच सामान्य deployment fault जल्दी पकड़ती है: updater की छोड़ी group-writable staging directory, signed app से बाहर restore हुआ helper, या developer script का symbolic link। इन त्रुटियों को runner तक command पहुँचने से पहले रोकना चाहिए। चेतावनी देकर आगे बढ़ना packaging error को executable selection logic बना देता है।
केवल signature validity नहीं, पहचान सत्यापित करें
Valid signature बताता है कि signed code अब भी उस signature के अनुरूप है। वह यह नहीं बताता कि code आपकी team ने sign किया या executable आपका helper है। macOS पर code ad hoc signed हो सकता है, और कोई दूसरा developer भी पूरी तरह valid signed code बना सकता है।
Code Signing Services को एक explicit requirement के साथ उपयोग करें, जो distribution channel की अपेक्षित signing identifier और team identity बताए। Apple TN3127 अंतर साफ करता है: code-signing identifier signer का चुना नाम है, signing identity में certificate और private key होते हैं, और designated requirement बताती है कि release बदलने पर किसे वही code माना जाए। स्क्रीन पर दिखा Authority string diagnostic shortcut है, product security boundary नहीं।
Absolute helper URL के लिए SecStaticCode बनाएँ, requirement compile या load करें और SecStaticCodeCheckValidityWithErrors चलाएँ। kSecCSStrictValidate और kSecCSCheckAllArchitectures शामिल करें। Apple कहता है कि default में universal binary की केवल native slice जाँची जा सकती है। सभी slices जाँचने से untested architecture पर अलग या टूटा signature छिपा नहीं रहता।
इस काम के लिए kSecCSBasicValidateOnly न उपयोग करें। यह flag main executable और resources की जाँच छोड़ देता है, जिससे integrity check का उद्देश्य खत्म हो जाता है। Gateway के नियंत्रण वाली requirement से तुलना किए बिना helper की अपनी designated requirement पर भरोसा न करें। अपना परिचय देना authorization नहीं है।
बाहरी ऐप को भी सत्यापित करें। Apple helper tool को standard nested-code location में रखता है ताकि signing machinery उसे code माने। सही signed release में intent की chain होती है: helper अपनी expected requirement पूरी करता है और outer app का seal उस nested component को दर्ज करता है। दोनों जाँचने से ऐसा helper पकड़ा जाता है जो स्वयं valid है पर damaged bundle में लाया गया है।
विस्तृत CFError भीतर रखें और user के लिए उसे छोटी refusal category में बदलें: signature गायब, requirement mismatch, invalid resource, unsupported architecture या बदली फ़ाइल। Validation error पर किसी दूसरे path से retry कभी न करें। Code-signing failure का अर्थ runner उपलब्ध नहीं है।
Static जाँच लॉन्च race बंद नहीं कर सकती
Static validation तभी सही है जब फ़ाइल बदली न हो। Apple SecStaticCodeCheckValidity के दस्तावेज़ में इसे सीधे कहता है और network, union तथा FUSE जैसे dynamic filesystem का नाम लेता है। यही चेतावनी साधारण replaceable directory entry पर भी लागू है: validation लौटने के बाद कोई प्रक्रिया posix_spawn द्वारा path resolve होने से पहले दूसरा executable वहाँ rename कर सकती है।
यह failure चार कदम में होता है:
- Gateway
/Applications/Example.app/Contents/Helpers/runnerresolve करके file A सत्यापित करता है। - दूसरी प्रक्रिया file B को उसी pathname पर rename करती है।
- Gateway
Processयाposix_spawnसे pathname चलाने को कहता है। - Kernel file B खोलता है, क्योंकि spawn call को file A का रखा descriptor नहीं, उसका नाम मिला था।
Validation से पहले और बाद stat तुलना कई प्रयास पकड़ती और window घटाती है, लेकिन कदम दो और तीन को indivisible नहीं बनाती। खुद hash बनाना भी यही सीमा रखता है और code signature का काम दोहराता है। Lock file केवल सहमत process को coordinate करता है। attacker lock नहीं मानेगा।
एक आम सलाह है कि app startup पर एक बार validate करके सफलता cache कर लें। यह लोकप्रिय है क्योंकि signature check की लागत होती है और bundled code सामान्य run में immutable लगता है। Gateway के लिए यह गलत है। Menu-bar app चलते समय update, restore, volume change या replacement हो सकता है। Apple की सलाह के अनुसार compiled requirement object cache करें, पर हर launch boundary पर executable फिर evaluate करें।
खुला descriptor फिर भी काम आता है। Validation के दौरान उसे रखें, spawn से तुरंत पहले फिर fstat करें, और device, inode, size, modification time या change time बदलने पर इनकार करें। Process बनने के बाद telemetry के लिए तीसरा snapshot लें। ये तुलना diagnosis सुधारती और attack कठिन करती हैं, लेकिन claim सही रखें: केवल operating-system launch requirement identity को process launch से बाँधती है।
Requirement को process creation से बाँधें
LightweightCodeRequirements वाले system पर run से पहले Process.launchRequirement सेट करें। Apple कहता है कि launching executable यदि LaunchCodeRequirement पूरी नहीं करता तो operating system process नहीं चलाता और crash report बनाता है। निर्णायक identity check process creation के भीतर जाने से pathname selection निर्णय से अलग नहीं हो सकता।
मुख्य Swift setup छोटा है:
import Foundation
import LightweightCodeRequirements
func configuredProcess(helper: URL, team: String, identifier: String) throws -> Process {
let requirement = try LaunchCodeRequirement.allOf {
ValidationCategory(.developerID)
TeamIdentifier(team)
SigningIdentifier(identifier)
}
let process = Process()
process.executableURL = helper
process.launchRequirement = requirement
return process
}
Team और identifier signed gateway में compile की गई release configuration से आने चाहिए। इन्हें helper के पास की preference से load न करें। कई signing channel हों तो हर supported channel के लिए स्पष्ट requirement बनाएँ और test करें, एक expression को तब तक कमजोर न करें जब तक हर build pass न हो जाए।
Requirement Mach-O helper पर लगाएँ, shell wrapper पर नहीं। Apple बताता है कि shebang script में launch requirement interpreter को जाँचती है। /bin/bash का Apple code होना उन script bytes को विश्वसनीय नहीं बनाता जिन्हें आप चलाना चाहते हैं। Scripting logic signed executable code में रखें, या script को trusted code के भीतर पढ़ा गया sealed data मानें और privileged runner की तरह launch न करें।
API को SDK availability के अनुसार feature-gate करें और diagnosis के लिए static preflight रखें। पुराने deployment target पर POSIX_SPAWN_START_SUSPENDED child को user-space execution से पहले रोक सकता है। उसके PID का dynamic SecCode लेकर requirement जाँचें, फिर resume या kill करें। यह fallback नाज़ुक है: हर return code जाँचें, PID confusion रोकें, अनचाहे descriptor बंद करें और validation से पहले command या credential न भेजें। यदि threat model यह जटिलता स्वीकार नहीं करता, तो launch requirement वाला OS version अनिवार्य करें।
साफ और सीमित process contract के साथ लॉन्च करें
Binary सत्यापन उससे मिलने वाले input को सुरक्षित नहीं बनाता। Argument vector structured field से बनाएँ, explicit environment सेट करें, working directory जानबूझकर चुनें और helper को केवल ज़रूरी descriptor दें। SSH command बनाने के लिए shell कभी न चलाएँ।
खाली environment या allowlist से शुरू करें। Dynamic loading, configuration discovery, locale, proxy या home lookup को बदलने वाले variable signed program का व्यवहार code बदले बिना बदल सकते हैं। Hardened Runtime और Library Validation कुछ loading attack घटाते हैं, पर arbitrary inherited environment को सुरक्षित interface नहीं बनाते।
Standard input और output को protocol मानें। Maximum message size तय करें, extra field अस्वीकार करें, exchange पर deadline रखें और helper protocol error को SSH exit status से अलग करें। Stateless helper को environment variable, argument, temporary file या agent-provided path से key नहीं पढ़नी चाहिए। उसे controlled channel पर न्यूनतम request मिले और न्यूनतम result लौटाना चाहिए।
हर असंबंधित descriptor बंद करें। Preflight में खुली file पर O_CLOEXEC उपयोगी है, लेकिन बाकी app का audit भी करें। Vault database descriptor, IPC listener या log file पाने वाले child को ऐसी पहुँच मिली है जो signature check ने देने का इरादा नहीं रखा। Contract अनुमति दे तो resource limit लगाएँ और cancellation या timeout पर पूरा child process group समाप्त करें।
Decision input log करें, secrets नहीं: resolved bundle-relative location, signing requirement version, device और inode snapshot, validation result, child PID और termination reason। Record बताए कि कौन-सा executable launch authorized था। केवल textual path लिखने से वह तथ्य खो जाता है कि incident में एक नाम कई files को बता सकता था।
सत्यापन को authorization क्रम के भीतर रखें
Helper acceptance उसी transaction में होनी चाहिए जिसमें action authorization है, और gateway के child को कोई capability देने से पहले। App खुलने पर जाँच बहुत जल्दी है। Remote command की approval के बाद जाँच बहुत देर है यदि failure handling command leak, socket open या fallback कर सकती है।
एक launch को स्पष्ट, irreversible boundary वाली state machine मानें। Gateway action लेता और parse करता है जब request को credential access नहीं है। वह vault या credential source उपलब्ध होने की पुष्टि करता है, helper resolve और preflight करता है, ज़रूरी human authorization लेता है, फिर attached code requirement के साथ launch करता है। Launch सफल होने के बाद ही verified child के लिए न्यूनतम credential या connection channel बनता है। पहले की किसी state की failure pending action नष्ट कर देती है।
User approval की जगह उसके अर्थ पर निर्भर है। यदि card पूछता है कि कोई agent खास SSH command चला सकता है, तो महँगे launch से पहले दिख सकता है। Approved command बाद में immutable रहे और helper validation failure उस approval को consume या cancel करे, उसे बाद की binary के लिए queue न करे। यदि approval उस process पर trust बताती है जो action करेगा, तो candidate identity मिलने के बाद ही दिखाएँ। दोनों design चल सकते हैं, पर audit record request digest, authorization decision, signing requirement और child process को एक attempt में जोड़ना चाहिए।
Process creation success मिलते ही secret न दें। API माँगे तो spawn से पहले pipe या socket तैयार करें, लेकिन credential वाला सिरा बंद या gated रखें। Launch enforcement success की प्रतीक्षा करें, child identity दर्ज करें, फिर request भेजें। Child को SSH key operation चाहिए तो private key bytes उसकी memory में copy करने के बजाय सीमित signing या connection interface दें। Helper को जितना कम अधिकार मिलेगा, पिछली किसी जाँच की भूल का असर उतना छोटा होगा।
Cancellation पर भी यही सावधानी चाहिए। User helper validation के दौरान session revoke कर सकता है, या agent approval के बाद spawn से पहले disconnect कर सकता है। Launch से तुरंत पहले authorization generation और request release से पहले फिर जाँचें। बदलने पर child terminate करके channel बंद करें। यह filesystem race नहीं, पर इसी security decision के आसपास दूसरा check/use gap है।
Concurrent launch mutable configuration साझा न करें। हर attempt का immutable helper URL, requirement reference, argument vector, environment, descriptor, deadline और audit identifier हो। एक thread global Process template बदले और दूसरा run करे तो दोनों binary valid होने पर भी approved request गलत executable या environment को मिल सकती है। Approval consume और configured process start करने वाले छोटे transition को synchronize करें, पूरे SSH connection lifetime को नहीं।
एक अच्छा invariant है कि identity enforcement से पहले child-controlled byte gateway की privileged state में न जाए। Helper run होते ही pipe पर लिख सकता है, इसलिए acceptance तक output parser अलग रखें, pipe buffer exposure सीमित करें और early output को protocol violation मानें। Unverified child के error text से privileged path, credential या अगली binary न चुनें।
यह क्रम साफ journal भी देता है। एक attempt request_received, candidate_preflight_passed, authorization_granted, launch_requirement_passed, request_released और terminal result दिखा सकता है। Missing transition दिखेंगे। Approval से सीधे SSH exit code पर जाने वाला record यह नहीं बताता कि expected runner ने command संभाली भी थी या नहीं।
वास्तव में शुरू हुए child को सत्यापित करें
Launch enforcement निर्णायक gate होना चाहिए, पर post-launch observation integration error पकड़ता और responder को stable process identity देता है। run सफल होने पर PID लें और running process का dynamic SecCode पाएँ। Protocol parser जोड़ने या sensitive input देने से पहले corresponding process requirement से उसे जाँचें।
Apple का SecStaticCode और SecCode का अंतर यहाँ उपयोगी है। Static object disk पर code बताता है और running code से स्वाभाविक रूप से जुड़ा नहीं। Dynamic code object process में loaded code बताता है। दोनों अलग सवाल पूछते हैं: static check installed candidate की integrity समझाती है, dynamic check उस child की identity बताती है जिसे macOS ने अभी बनाया।
केवल PID lookup की धार तेज है। PID दोबारा उपयोग होते हैं और short-lived child run, lookup और validation के बीच exit कर सकता है। Code object न मिले या validate न हो तो launch failure मानें। IPC API audit token दे तो message में मिले PID के बजाय वह मजबूत process reference उपयोग करें। Child की बताई PID, signing identifier या executable path पर भरोसा न करें।
जहाँ child पहले CPU पा सकता है, post-launch check अकेली जाँच नहीं बननी चाहिए। Substituted process spawn और inspection के बीच काम कर सकता है। पुराने system पर user-space execution से पहले suspension gap घटाता है, पर implementation केवल validated PID resume करे और हर error path पर kill करे। Launch requirement आसान है क्योंकि OS mismatch को चलने से पहले रोकता है।
Acceptance के बाद process handle रखें और हर protocol message उसी instance से बाँधें। Named socket फिर न खोलें, न ऐसे helper endpoint से reconnect करें जिसे कोई और process ले सकता है। Child exit पर channel बंद करें, action invalidate करें और अगले process के लिए नया verified launch माँगें। पहला valid child बाद में वही PID या endpoint name पाने वाले child को authorize नहीं करता।
Installed और running दोनों evidence दर्ज करें। Installed object के लिए bundle-relative path, device, inode, size, timestamp और static validation outcome रखें। Process के लिए PID, signing requirement revision, launch enforcement result, dynamic validation outcome, start time और termination status। ये secrets नहीं हैं और command या credential dump किए बिना corrupt release, update collision, requirement error और substitution अलग करने में मदद करते हैं।
Process identity का lifetime rule भी हो। यदि helper दूसरा program exec करता है, verified identity नई image को अपने आप नहीं मिलती। ऐसा design न रखें जिसमें signed wrapper सही validate हो और फिर PATH से arbitrary ssh चलाए। Exec transition ज़रूरी हो तो final executable का अपना enforced requirement और fixed path हो, या trusted helper स्वयं protocol करे। Launcher पर signature उसके बाद चुने program का प्रमाण नहीं है।
Library और configuration भी देखें। Running main executable requirement पूरी कर सकता है, फिर भी unsafe environment variable या writable search location उसका व्यवहार बदल सकती है। Dependent code sign करें, helper के लिए उचित Hardened Runtime option सक्षम करें, compatible होने पर Library Validation लें और environment से search path हटाएँ। Configuration को gateway का validated input रखें, child को home directory में dotfile खोजने न दें।
अंत में telemetry के दावे का परीक्षण करें। Replacement-race test में static snapshot file A पहचाने, launch enforcement या तो A चलाए या B अस्वीकार करे, और dynamic check launch result से सहमत हो। यदि log कह सकता है कि A pass हुआ जबकि बिना record वाला B चला, evidence model अब भी process के बजाय pathname देख रहा है।
विफलता पर बंद रहें, पर update संभव रखें
हर verification failure उस command runner को रोके और बाकी app को समझने योग्य अवस्था में छोड़े। System ssh पर fallback न करें, दूसरी directory न खोजें, launch requirement न हटाएँ, और user से unidentified helper approve न कराएँ। Approval executable identity ठीक नहीं कर सकती।
Update एक अलग state transition है। नया SSH काम लेना रोकें, active child को पूरा होने दें या terminate करें, पूरे signed bundle को atomic replacement से install करें, फिर bundle, file और signature की सारी जाँच दोहराएँ। अलग release का helper और app दोनों valid signed हो सकते हैं, फिर भी उनके बीच protocol contract टूट सकता है। Launch के बाद protocol version handshake रखें और action भेजने से पहले mismatch रोकें।
तय करें कौन-सा बदलाव user-visible recovery चाहता है। Partial update के बाद missing helper में app reinstall करना पड़ सकता है। Team या identifier mismatch tampering हो सकता है और अधिक स्पष्ट message चाहता है। Mode mismatch backup tool से आ सकता है। भीतर exact error रखें, बाहर छोटा action बताएँ जो user को check bypass करना न सिखाए।
सिर्फ happy-path signature नहीं, replacement व्यवहार test करें। Release suite में helper path का symlink, दूसरा hard link, group-writable parent, ad hoc signed substitute, गलत identifier से सही signed binary, damaged non-native slice वाला universal file, preflight और launch के बीच replacement और app चलते समय update शामिल हों। Static validation के बाद pause करने वाला test hook race को luck के बिना दोहराता है।
Sallyport अपने SSH channel में bundled stateless Go helper उपयोग करता है, जबकि vault core signed menu-bar app के भीतर रहता है। यह विभाजन secrets को helper से दूर रखता है, लेकिन helper verification को वैकल्पिक नहीं बनाता: approved action देने से पहले gateway को साबित करना होगा कि कौन-सा command runner शुरू हुआ।
Design review की acceptance rule एक पंक्ति में आनी चाहिए: running bundle की expected file descriptor और directory checks पास करती है, release signing requirement पूरी करती है, और process creation उसी identity को enforce करता है। Platform अंतिम बात enforce न कर सके तो कमजोर guarantee लिखें और child को मिलने वाली चीज़ें घटाएँ।
सामान्य प्रश्न
सिर्फ SSH helper path जाँचना पर्याप्त क्यों नहीं है?
Path एक नाम है, स्थिर file identity नहीं। दूसरी process जाँच के बाद directory entry बदल सकती है, इसलिए gateway को खुली file जाँचनी और process launch पर code requirement लागू करनी चाहिए।
क्या macOS app को PATH से helper खोजना चाहिए?
नहीं। PATH environment को executable code चुनने देता है और packaging failure को सफल discovery बना देता है। Running app bundle से एक exact helper URL तय करें और हर fallback रोकें।
Gateway को कौन-सा file metadata जाँचना चाहिए?
O_NOFOLLOW के साथ खोलने के बाद fstat से पुष्टि करें कि object regular executable file है, expected owner, सुरक्षित write permission और expected hard-link count रखता है। बाद की तुलना और logging के लिए device और inode रखें।
क्या valid code signature साबित करता है कि helper मेरा है?
नहीं। Validity केवल signature से consistency बताती है, और macOS दूसरे developer या ad hoc signed code चला सकता है। Expected signing identifier, team और distribution category की explicit requirement लगाएँ।
Universal helper की हर architecture क्यों जाँचें?
Apple कहता है कि static validation आम तौर पर केवल native architecture जाँचती है। kSecCSCheckAllArchitectures हर slice जाँचता है, जिसमें दूसरी machine या explicit architecture selection द्वारा चल सकने वाली slice भी है।
क्या खुला file descriptor replacement race समाप्त करता है?
वह inspected file को स्थिर रखता है, पर सामान्य Process और posix_spawn executable को path से चुनते हैं। Snapshot और diagnosis के लिए descriptor रखें, फिर identity को process creation से बाँधने के लिए launch-time requirement उपयोग करें।
Process.launchRequirement क्या जोड़ता है?
यह operating system से launch के दौरान executable पर LaunchCodeRequirement evaluate कराता है। Failure पर OS process नहीं चलाता, जिससे static path check का gap बंद होता है।
Code signing के साथ ownership और permission अभी उपयोगी हैं?
हाँ, वे unsafe installation state पकड़ते और replacement का मौका घटाते हैं। वे supporting evidence हैं, signer identity या launch-time enforcement का विकल्प नहीं।
क्या app सफल helper verification cache करे?
माप से लाभ हो तो compiled requirement cache करें, लेकिन हर launch boundary पर executable फिर validate करें। Long-running app update, restore, mount change या जानबूझकर replacement के दौरान चल सकता है।
Helper verification विफल हो तो gateway क्या करे?
उस runner को रोकें, exact internal reason log करें और damaged app reinstall करने जैसी उचित recovery दें। Action चलाने के लिए दूसरा SSH binary न खोजें और requirement कमजोर न करें।