Apple ld Manual Page

I’ve exported the ld man page from MacOS 13 as it doesn’t seem to be easy to find on the web, so thought it may be useful to replicate here.

Note that this is reproduced as-is for convenience, so the copyright stays with the original owner.

NAME
     ld – linker

SYNOPSIS
     ld files...  [options] [-o outputfile]

DESCRIPTION
     The ld command combines several object files and libraries, resolves references, and produces an
     output file.  ld can produce a final linked image (executable, dylib, or bundle), or with the -r
     option, produce another object file.  If the -o option is not used, the output file produced is
     named "a.out".

   Universal
     The linker accepts universal (multiple-architecture) input files, but always creates a "thin"
     (single-architecture), standard Mach-O output file.  The architecture for the output file is
     specified using the -arch option.  If this option is not used, ld attempts to determine the output
     architecture by examining the object files in command line order.  The first "thin" architecture
     determines that of the output file.  If no input object file is a "thin" file, the native 32-bit
     architecture for the host is used.

     Usually, ld is not used directly.  Instead the compiler driver invokes ld. The compiler driver can
     be passed multiple -arch options and it will create a universal final linked image by invoking ld
     multiple times and then running lipo(1) merge the outputs into a universal file.

   Layout
     The object files are loaded in the order in which they are specified on the command line.  The
     segments and the sections in those segments will appear in the output file in the order they are
     encountered in the object files being linked.  All zero fill sections will appear after all non-zero
     fill sections in their segments.

   Libraries
     A static library (aka static archive) is a collection of .o files with a table of contents that
     lists the global symbols in the .o files.  ld will only pull .o files out of a static library if
     needed to resolve some symbol reference.  Unlike traditional linkers, ld will continually search a
     static library while linking. There is no need to specify a static library multiple times on the
     command line.

     A dynamic library (aka dylib or framework) is a final linked image.  Putting a dynamic library on
     the command line causes two things: 1) The generated final linked image will have encoded that it
     depends on that dynamic library. 2) Exported symbols from the dynamic library are used to resolve
     references.
Both dynamic and static libraries are searched as they appear on the command line.

   Search paths
     ld maintains a list of directories to search for a library or framework to use.  The default library
     search path is /usr/lib then /usr/local/lib.  The -L option will add a new library search path.  The
     default framework search path is /Library/Frameworks then /System/Library/Frameworks.  (Note:
     previously, /Network/Library/Frameworks was at the end of the default path.  If you need that
     functionality, you need to explicitly add -F/Network/Library/Frameworks).  The -F option will add a
     new framework search path.  The -Z option will remove the standard search paths.  The -syslibroot
     option will prepend a prefix to all search paths.

   Two-level namespace
     By default all references resolved to a dynamic library record the library to which they were
     resolved. At runtime, dyld uses that information to directly resolve symbols.  The alternative is to
     use the -flat_namespace option.  With flat namespace, the library is not recorded.  At runtime, dyld
     will search each dynamic library in load order when resolving symbols. This is slower, but more like
     how other operating systems resolve symbols.

   Indirect dynamic libraries
     If the command line specifies to link against dylib A, and when dylib A was built it linked against
     dylib B, then B is considered an indirect dylib.  When linking for two-level namespace, ld does not
     look at indirect dylibs, except when re-exported by a direct dylibs.  On the other hand when linking
     for flat namespace, ld does load all indirect dylibs and uses them to resolve references.  Even
     though indirect dylibs are specified via a full path, ld first uses the specified search paths to
     locate each indirect dylib.  If one cannot be found using the search paths, the full path is used.

   Dynamic libraries undefines
     When linking for two-level namespace, ld does not verify that undefines in dylibs actually exist.
     But when linking for flat namespace, ld does check that all undefines from all loaded dylibs have a
     matching definition.  This is sometimes used to force selected functions to be loaded from a static
     library.

OPTIONS
   Options that control the kind of output
     -execute
             The default.  Produce a mach-o main executable that has file type MH_EXECUTE.

     -dylib  Produce a mach-o shared library that has file type MH_DYLIB.

     -bundle
             Produce a mach-o bundle that has file type MH_BUNDLE.
     -r      Merges object files to produce another mach-o object file with file type MH_OBJECT.

     -dylinker
             Produce a mach-o dylinker that has file type MH_DYLINKER.  Only used when building dyld.

     -dynamic
             The default.  Implied by -dylib, -bundle, or -execute

     -static
             Produces a mach-o file that does not use the dyld.  Only used building the kernel.

     -preload
             Produces a mach-o file in which the mach_header, load commands, and symbol table are not in
             any segment.  This output type is used for firmware or embedded development where the
             segments are copied out of the mach-o into ROM/Flash.

     -arch arch_name
             Specifies which architecture (e.g. ppc, ppc64, i386, x86_64) the output file should be.

     -o path
             Specifies the name and location of the output file.  If not specified, `a.out' is used.

   Options that control libraries
     -lx     This option tells the linker to search for libx.dylib or libx.a in the library search path.
             If string x is of the form y.o, then that file is searched for in the same places, but
             without prepending `lib' or appending `.a' or `.dylib' to the filename.

     -needed-lx
             This is the same as the -lx but means to really link with the dylib even if no symbols are
             used from it.  Thus, it can be used suppress warnings about unused dylibs.

     -reexport-lx
             This is the same as the -lx but specifies that the all symbols in library x should be
             available to clients linking to the library being created.  This was previously done with a
             separate -sub_library option.

     -upward-lx
             This is the same as the -lx but specifies that the dylib is an upward dependency.

     -hidden-lx
             This is the same as the -lx for locating a static library, but treats all global symbols
             from the static library as if they are visibility hidden.  Useful when building a dynamic
             library that uses a static library but does not want to export anything from that static
             library.

     -weak-lx
             This is the same as the -lx but forces the library and all references to it to be marked as
             weak imports.  That is, the library is allowed to be missing at runtime.

     -assert-weak-lx
             This is the same as the -weak-l but verifies that all references were marked as weak
             imports, instead of forcing it.

     -delay-lx
             This is the same as the -lx but specifies that the dylib should be delay initialized.

     -needed_library path_to_dylib
             This is the same as placing path_to_dylib on the link line but means to really link with the
             dylib even if no symbols are used from it.  Thus, it can be used suppress warnings about
             unused dylibs.

     -reexport_library path_to_library
             This is the same as listing a file name path to a library on the link line and it specifies
             that the all symbols in library path should be available to clients linking to the library
             being created.  This was previously done with a separate -sub_library option.

     -upward_library path_to_library
             This is the same as listing a file name path to a library on the link line but also marks
             the dylib as an upward dependency.

     -weak_library path_to_library
             This is the same as listing a file name path to a library on the link line except that it
             forces the library and all references to it to be marked as weak imports.

     -assert_weak_library path_to_library
             This is the same as the -weak_library but verifies that all references were marked as weak
             imports, instead of forcing it.

     -delay_library path_to_library
             This is the same as listing a file name path to a library on the link line except that will
             mark the dylib to be delay initialized.

     -Ldir   Add dir to the list of directories in which to search for libraries.  Directories specified
             with -L are searched in the order they appear on the command line and before the default
             search path. In Xcode4 and later, there can be a space between the -L and directory.

     -Z      Do not search the standard directories when searching for libraries and frameworks.

     -syslibroot rootdir
             Prepend rootdir to all search paths when searching for libraries or frameworks.

     -search_paths_first
             This is now the default (in Xcode4 tools).  When processing -lx the linker now searches each
             directory in its library search paths for `libx.dylib' then `libx.a' before the moving on to
             the next path in the library search path.

     -search_dylibs_first
             Changes the searching behavior for libraries.  The default is that when processing -lx the
             linker searches each directory in its library search paths for `libx.dylib' then `libx.a'.
             This option changes the behavior to first search for a file of the form `libx.dylib' in each
             directory in the library search path, then a file of the form `libx.a' is searched for in
             the library search paths.  This option restores the search behavior of the linker prior to
             Xcode4.

     -framework name[,suffix]
             This option tells the linker to search for `name.framework/name' the framework search path.
             If the optional suffix is specified the framework is first searched for the name with the
             suffix and then without (e.g. look for `name.framework/name_suffix' first, if not there try
             `name.framework/name').

     -needed_framework name[,suffix]
             This is the same as the -framework name[,suffix] but means to really link with the framework
             even if no symbols are used from it.  Thus, it can be used suppress warnings about unused
             dylibs.

     -weak_framework name[,suffix]
             This is the same as the -framework name[,suffix] but forces the framework and all references
             to it to be marked as weak imports.  Note: due to a clang optimizations, if functions are
             not marked weak, the compiler will optimize out any checks if the function address is NULL.

     -assert_weak_framework name[,suffix]
             This is the same as the -weak_framework but verifies that all references were marked as weak
             imports, instead of forcing it.

     -reexport_framework name[,suffix]
             This is the same as the -framework name[,suffix] but also specifies that the all symbols in
             that framework should be available to clients linking to the library being created.  This
             was previously done with a separate -sub_umbrella option.

     -upward_framework name[,suffix]
             This is the same as the -framework name[,suffix] but also specifies that the framework is an
             upward dependency.

     -delay_framework name[,suffix]
             This is the same as the -framework name[,suffix] but also specifies that the framework
             should be delay initialized.

     -Fdir   Add dir to the list of directories in which to search for frameworks.  Directories specified
             with -F are searched in the order they appear on the command line and before the default
             search path. In Xcode4 and later, there can be a space between the -F and directory.

     -all_load
             Loads all members of static archive libraries.

     -ObjC   Loads all members of static archive libraries that implement an Objective-C class, category
             or a Swift struct, class or an extesion.

     -force_load path_to_archive
             Loads all members of the specified static archive library.  Note: -all_load forces all
             members of all archives to be loaded.  This option allows you to target a specific archive.

     -load_hidden path_to_archive
             Uses specified static library as usual, but treats all global symbols from the static
             library to as if they are visibility hidden.  Useful when building a dynamic library that
             uses a static library but does not want to export anything from that static library.

     -image_suffix suffix
             Search for libraries and frameworks with suffix and then without.

   Options that control additional content
     -sectcreate segname sectname file
             The section sectname in the segment segname is created from the contents of file file. If
             there's a section (segname,sectname) from any other input, the linker will append the
             content from the file to that section.

     -add_empty_section segname sectname
             An empty section named sectname in the segment segname. If any of the inputs contains a
             section (segname,sectname), that section will be included in the output, and this option
             will be ignored.

     -add_ast_path file
             The linker will add a N_AST stab symbol to the output file where the string is the path
             pointed by file and its values is the modification time of the file.

     -filelist file[,dirname]
             Specifies that the linker should link the files listed in file.  This is an alternative to
             listing the files on the command line.  The file names are listed one per line separated
             only by newlines. (Spaces and tabs are assumed to be part of the file name.)  If the
             optional directory name, dirname is specified, it is prepended to each name in the list
             file.

     -dtrace file
             Enables dtrace static probes when producing a final linked image.  The file file must be a
             DTrace script which declares the static probes.

   Options that control optimizations
     -dead_strip
             Remove functions and data that are unreachable by the entry point or exported symbols.

     -order_file file
             Alters the order in which functions and data are laid out.  For each section in the output
             file, any symbol in that section that are specified in the order file file is moved to the
             start of its section and laid out in the same order as in the order file file.  Order files
             are text files with one symbol name per line.  Lines starting with a # are comments.  A
             symbol name may be optionally preceded with its object file leaf name and a colon (e.g.
             foo.o:_foo).  This is useful for static functions/data that occur in multiple files.  A
             symbol name may also be optionally preceded with the architecture (e.g. ppc:_foo or
             ppc:foo.o:_foo).  This enables you to have one order file that works for multiple
             architectures.  Literal c-strings may be ordered by by quoting the string (e.g. "Hello,
             world\n") in the order file.

     -no_order_inits
             When the -order_file option is not used, the linker lays out functions in object file order
             and it moves all initializer routines to the start of the __text section and terminator
             routines to the end. Use this option to disable the automatic rearrangement of initializers
             and terminators.

     -platform_version platform min_version sdk_version
             This is set to indicate the platform, oldest supported version of that platform that output
             is to be used on, and the SDK that the output was built against.  platform is a numeric
             value as defined in <mach-o/loader.h>, or it may be one of the following strings:
             • macos
             • ios
             • tvos
             • watchos
             • bridgeos
             • visionos
             • xros
             • mac-catalyst
             • ios-simulator
             • tvos-simulator
             • watchos-simulator
             • visionos-simulator
             • xros-simulator
             • driverkit
             • firmware
             • sepOS
             Specifying a newer min or SDK version enables the linker to assume features of that OS or
             SDK in the output file. The format of min_version and sdk_version is a version number such
             as 10.13 or 10.14

     -macos_version_min version
             This is set to indicate the oldest macOS version that that the output is to be used on.
             Specifying a later version enables the linker to assumes features of that OS in the output
             file.  The format of version is a macOS version number such as 10.9 or 10.14

     -ios_version_min version
             This is set to indicate the oldest iOS version that that the output is to be used on.
             Specifying a later version enables the linker to assumes features of that OS in the output
             file.  The format of version is an iOS version number such as 3.1 or 4.0

     -image_base address
             Specifies the preferred load address for a dylib or bundle. The argument address is a
             hexadecimal number with an optional leading 0x.  By choosing non-overlapping address for all
             dylibs and bundles that a program loads, launch time can be improved because dyld will not
             need to "rebase" the image (that is, adjust pointers within the image to work at the loaded
             address).  It is often easier to not use this option, but instead use the rebase(1) tool,
             and give it a list of dylibs.  It will then choose non-overlapping addresses for the list
             and rebase them all. When building a position independent executable, this option will be
             ignored.  This option is also called -seg1addr for compatibility.

     -no_implicit_dylibs
             When creating a two-level namespace final linked image, normally the linker will hoist up
             public dylibs that are implicitly linked to make the two-level namespace encoding more
             efficient for dyld.  For example, Cocoa re-exports AppKit and AppKit re-exports Foundation.
             If you link with -framework Cocoa and use a symbol from Foundation, the linker will
             implicitly add a load command to load Foundation and encode the symbol as coming from
             Foundation.  If you use this option, the linker will not add a load command for Foundation
             and encode the symbol as coming from Cocoa.  Then at runtime dyld will have to search Cocoa
             and AppKit before finding the symbol in Foundation.

     -no_zero_fill_sections
             By default the linker moves all zero fill sections to the end of the __DATA segment and
             configures them to use no space on disk.  This option suppresses that optimization, so zero-
             filled data occupies space on disk in a final linked image.

    -merge_zero_fill_sections
             Causes all zero-fill sections in the __DATA segment to be merged into one __zerofill
             section.

     -no_branch_islands
             Disables linker creation of branch islands which allows images to be created that are larger
             than the maximum branch distance. Useful with -preload when code is in multiple sections but
             all are within the branch range.

     -O0     Disables certain optimizations and layout algorithms to optimize build time. This option
             should be used with debug builds to speed up incremental development. The exact
             implementation might change to match the intent.

     -reproducible
             By default output content will be deterministic, but small changes in input files such as a
             compilation time might affect certain data structures in the linked binary. This option
             instructs ld to create a reproducible output binary by ignoring certain input properties or
             using alternative algorithms.

   Options when creating a dynamic library (dylib)
     -install_name name
             Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked
             against the library will record that path as the way dyld should locate this library.  If
             this option is not specified, then the -o path will be used.  This option is also called
             -dylib_install_name for compatibility.

     -compatibility_version number
             Specifies the compatibility version number of the library.  When a library is loaded by
             dyld, the compatibility version is checked and if the program's version is greater that the
             library's version, it is an error.  The format of number is X[.Y[.Z]] where X must be a
             positive non-zero number less than or equal to 65535, and .Y and .Z are optional and if
             present must be non-negative numbers less than or equal to 255.  If the compatibility
             version number is not specified, it has a value of 0 and no checking is done when the
             library is used.  This option is also called -dylib_compatibility_version for compatibility.

     -current_version number
             Specifies the current version number of the library. The current version of the library can
             be obtained programmatically by the user of the library so it can determine exactly which
             version of the library it is using.  The format of number is X[.Y[.Z]] where X must be a
             positive non-zero number less than or equal to 65535, and .Y and .Z are optional and if
             present must be non-negative numbers less than or equal to 255.  If the version number is
             not specified, it has a value of 0.  This option is also called -dylib_current_version for
             compatibility.

   Options when creating a main executable
     -pie    This makes a special kind of main executable that is position independent (PIE).  On Mac OS
             X 10.5 and later, the OS the OS will load a PIE at a random address each time it is
             executed.  You cannot create a PIE from .o files compiled with -mdynamic-no-pic.  That means
             the codegen is less optimal, but the address randomization adds some security. When
             targeting Mac OS X 10.7 or later PIE is the default for main executables.

     -no_pie
             Do not make a position independent executable (PIE).  This is the default, when targeting
             10.6 and earlier.

     -pagezero_size size
             By default the linker creates an unreadable segment starting at address zero named
             __PAGEZERO.  Its existence will cause a bus error if a NULL pointer is dereferenced.  The
             argument size is a hexadecimal number with an optional leading 0x.  If size is zero, the
             linker will not generate a page zero segment.  By default on 32-bit architectures the page
             zero size is 4KB.  On 64-bit architectures, the default size is 4GB.

     -stack_size size
             Specifies the maximum stack size for the main thread in a program.  Without this option a
             program has a 8MB stack.  The argument size is a hexadecimal number with an optional leading
             0x. The size should be a multiple of the architecture's page size (4KB or 16KB).

     -allow_stack_execute
             Marks executable so that all stacks in the task will be given stack execution privilege.
             This includes pthread stacks. This option is only valid when targeting architectures that
             support stack execution (i.e. Intel).

     -export_dynamic
             Preserves all global symbols in main executables during LTO.  Without this option, Link Time
             Optimization is allowed to inline and remove global functions. This option is used when a
             main executable may load a plug-in which requires certain symbols from the main executable.

   Options when creating a bundle
     -bundle_loader executable
             This specifies the executable that will be loading the bundle output file being linked.
             Undefined symbols from the bundle are checked against the specified executable like it was
             one of the dynamic libraries the bundle was linked with.

   Options when creating an object file
     -keep_private_externs
             Don't turn private external (aka visibility=hidden) symbols into static symbols, but rather
             leave them as private external in the resulting object file.

     -d      Force definition of common symbols.  That is, transform tentative definitions into real
             definitions.

   Options that control symbol resolution
     -exported_symbols_list filename
             The specified filename contains a list of global symbol names that will remain as global
             symbols in the output file.  All other global symbols will be treated as if they were marked
             as __private_extern__ (aka visibility=hidden) and will not be global in the output file. The
             symbol names listed in filename must be one per line.  Leading and trailing white space are
             not part of the symbol name.  Lines starting with # are ignored, as are lines with only
             white space.  Some wildcards (similar to shell file matching) are supported.  The * matches
             zero or more characters.  The ? matches one character.  [abc] matches one character which
             must be an 'a', 'b', or 'c'.  [a-z] matches any single lower case letter from 'a' to 'z'.

     -exported_symbol symbol
             The specified symbol is added to the list of global symbols names that will remain as global
             symbols in the output file.  This option can be used multiple times.  For short lists, this
             can be more convenient than creating a file and using -exported_symbols_list.

     -no_exported_symbols
             Useful for main executable that don't have plugins and thus need no symbol exports.

     -unexported_symbols_list file
             The specified filename contains a list of global symbol names that will not remain as global
             symbols in the output file.  The symbols will be treated as if they were marked as
             __private_extern__ (aka visibility=hidden) and will not be global in the output file. The
             symbol names listed in filename must be one per line.  Leading and trailing white space are
             not part of the symbol name.  Lines starting with # are ignored, as are lines with only
             white space.  Some wildcards (similar to shell file matching) are supported.  The * matches
             zero or more characters.  The ? matches one character.  [abc] matches one character which
             must be an 'a', 'b', or 'c'.  [a-z] matches any single lower case letter from 'a' to 'z'.

     -unexported_symbol symbol
             The specified symbol is added to the list of global symbols names that will not remain as
             global symbols in the output file.  This option can be used multiple times.  For short
             lists, this can be more convenient than creating a file and using -unexported_symbols_list.

     -reexported_symbols_list file
             The specified filename contains a list of symbol names that are implemented in a dependent
             dylib and should be re-exported through the dylib being created.

     -alias symbol_name alternate_symbol_name
             Create an alias named alternate_symbol_name for the symbol symbol_name.  By default the
             alias symbol has global visibility.  This option was previous the -idef:indir option.

     -alias_list filename
             The specified filename contains a list of aliases. The symbol name and its alias are on one
             line, separated by whitespace.  Lines starting with # are ignored.

     -flat_namespace
             Alters how symbols are resolved at build time and runtime.  With -two_levelnamespace (the
             default), the linker only searches dylibs on the command line for symbols, and records in
             which dylib they were found.  With -flat_namespace, the linker searches all dylibs on the
             command line and all dylibs those original dylibs depend on.  The linker does not record
             which dylib an external symbol came from, so at runtime dyld again searches all images and
             uses the first definition it finds.  In addition, any undefines in loaded flat_namespace
             dylibs must be resolvable at build time.

     -u symbol_name
             Specified that symbol symbol_name must be defined for the link to succeed.  This is useful
             to force selected functions to be loaded from a static library.

     -U symbol_name
             Specified that it is ok for symbol_name to have no definition.  With -two_levelnamespace,
             the resulting symbol will be marked dynamic_lookup which means dyld will search all loaded
             images.

     -undefined treatment
             Specifies how undefined symbols are to be treated. Options are: error, warning, suppress, or
             dynamic_lookup.  The default is error. Note: dynamic_lookup that depends on lazy binding
             will not work with chained fixups.

     -rpath path
             Add path to the runpath search path list for image being created.  At runtime, dyld uses the
             runpath when searching for dylibs whose load path begins with @rpath/.

     -commons treatment
             Specifies how commons (aka tentative definitions) are resolved with respect to dylibs.
             Options are: ignore_dylibs, error.  The default is ignore_dylibs which means the linker will
             turn a tentative definition in an object file into a real definition and not even check
             dylibs for conflicts.  The error option means the linker should issue an error whenever a
             tentative definition in an object file conflicts with an external symbol in a linked dylib.
             See also -warn_commons.

   Options for introspecting the linker
     -why_load
             Log why each object file in a static library is loaded. That is, what symbol was needed.
             Also called -whyload for compatibility.

     -why_live symbol_name
             Logs a chain of references to symbol_name.  Only applicable with -dead_strip .  It can help
             debug why something that you think should be dead strip removed is not removed.  See
             -exported_symbols_list for syntax and use of wildcards.

     -print_statistics
             Logs information about the amount of memory and time the linker used.

     -t      Logs each file (object, archive, or dylib) the linker loads.  Useful for debugging problems
             with search paths where the wrong library is loaded.

     -order_file_statistics
             Logs information about the processing of a -order_file.

     -map map_file_path
             Writes a map file to the specified path which details all symbols and their addresses in the
             output image.

   Options for controlling symbol table optimizations
     -S      Do not put debug information (STABS or DWARF) in the output file.

     -x      Do not put non-global symbols in the output file's symbol table. Non-global symbols are
             useful when debugging and getting symbol names in back traces, but are not used at runtime.
             If -x is used with -r non-global symbol names are not removed, but instead replaced with a
             unique, dummy name that will be automatically removed when linked into a final linked image.
             This allows dead code stripping, which uses symbols to break up code and data, to work
             properly and provides the security of having source symbol names removed.

     -non_global_symbols_strip_list filename
             The specified filename contains a list of non-global symbol names that should be removed
             from the output file's symbol table.  All other non-global symbol names will remain in the
             output files symbol table. See -exported_symbols_list for syntax and use of wildcards.

     -non_global_symbols_no_strip_list filename
             The specified filename contains a list of non-global symbol names that should be remain in
             the output file's symbol table.  All other symbol names will be removed from the output
             file's symbol table. See -exported_symbols_list for syntax and use of wildcards.

     -oso_prefix prefix-path
             When generating the debug map, the linker will remove the specified prefix-path from the
             path in OSO symbols. This can be used so to help build servers generate identical binaries.
             If '.' is passed as argument, the linker will expand the argument to the current working
             directory.

Securing WordPress

WordPress is used by 43% of websites worldwide, so it’s an obvious target for hackers looking to compromise a site. Unfortunately, for lots of reasons mentioned in this article, admins aren’t making this difficult enough for hackers so it’s common to hear of WordPress sites being powned, filled with malware or customer details stolen from a website.

The good news is that it’s easy to protect your WordPress site with a few simple steps, and everything I’m suggesting is free to implement so I hope you find it useful in your fight again the hackers!

I’m writing this to help those who may be concerned about WordPress security. I aim to provide some insight, tools and options to improve your site security. I recently decided to try and improve our site security from DDoS and hack attempts as it was starting to affect the availability of one of our product sites (thankfully we haven’t been hacked) and I knew there must be good ways to better protect it.
This article covers what I discovered, some background from my research, and suggestions based on what we implemented that seem to have helped.

To start on a positive note, here’s the impact these changes made to our site:

Going from 200 attackers to 0 per day by protecting our site

Note: the details below are by no way exhaustive, as new attacks and techniques are invented every day. What is proposed here will give you a great fighting chance of avoiding being attacked but there’s still a small risk so you need to keep monitoring your site regularly for signs of attack and deal with any attempts as quickly as possible.

Attack Vectors

Before getting into what you can do to protect your site, it’s useful to understand what the 2 biggest risks are (and why), so you can see how hackers are compromising vulnerable sites.

1. Passwords

There are infinite combinations of letters, numbers and symbols that can be used to create a password, but somehow a hacker can still seem to “guess” those that are compromised. How do they do this?

First of all, they use an “most frequently used” attack. This means that they basically have a long list of the passwords that people often use, and try each one (automated with a bot often). Ever wondered why you shouldn’t use Password123? That’s because it’s item number 1 on this list!

Often they hacker will also try variants of these, so you may have used Password27 thinking that’s ok… nope. The hacker will try adding different incremental numbers to each password in the list because people tend to just add incremental numbers to their password each time they need to change it. Password17 becomes Password18 so it’s easy to remember.

Once the hacker has exhausted this list, they can perform a Dictionary Attack. This mean they try common words from the dictionary with variations of capital letters and numbers, as this is a common way to create passwords.
Did you think G4r4g3 was more secure than Garage123 as a password? Nope. As most people now swap certain letters for numbers, hackers have become wise to this and will try those combinations. G4r4g3! isn’t any better as they will also try adding ! to the end as this is a common password technique.

There’s also the personal and username attacks that are used. For example, to help people remember their passwords, it isn’t uncommon to include part of the username in the password. A user called Robert may try to use a password of Rob3rt22 as he’ll remember that. That’s something the hacker will try.
In the same vein, a hacker will also try to find valid user accounts by trying the name of those people they know work at the organisation, so having a user called Robert Smith with a username or “rsmith”, “Robert.smith” or similar will make it easy for a hacker to find a valid user account in the first place that they can then try to discover the password for using the above techniques.

If all else fails, and the hacker hasn’t compromised the account using the above techniques, they use good-old Brute Force, where they just try every combination of letters, words, numbers etc until they’re in. Given how fast modern computers are, this can be a successful way of getting in unless you protect yourselves from this (which is also very easy).

Time it takes a hacker to compromise your password

2. Vulnerabilities in Software

No developer wants to introduce bugs in their code or things that can be exploited but unfortunately it’s a constant 2-way battle between increasingly complex software which becomes exponentially harder to test, and hackers who really pride themselves on find a flaw in complicated systems. This is why companies with deep pockets like Apple, Microsoft and Google pay bounties for any security flaw a hacker discovers in their systems – they actually pay hackers to hack their systems to prove how unhackable they are!

But in the real world, developers can only do their best and fix issues they’re aware of so often a new vulnerability is discovered and a new patch or update is delivered by the developers to patch it. These are called zero day vulnerabilities if they’ve only just been discovered.

So in short, the worst thing you can do is forget about your site once it’s been created. You need to make sure you keep maintaining it with the latest patches, updates and changes to keep it secure. The most common WordPress compromises you hear of will be on sites that rarely become maintained. Sites where the author wrote their articles then haven’t touched it for a while.

Securing Your Site

There may only be 2 vectors mentioned above but each includes a wide range of ways an attacker can use those to get into your site, so you will need to employ a number of changes to secure yourself properly.

1. Update your PHP, WordPress and Plugin versions

WordPress is written using the PHP framework. PHP has been around forever (or feels like it) and wasn’t originally designed with hackers and security in mind, so contained a lot of easy ways to attack sites built with it. It has improved but it’s always best to use the latest version you can. At time of writing the most widely supported version is v8.1 so make sure you upgrade to that ASAP.

WordPress itself is constantly being updated with new security patches and features to make sure you’re using the latest version of that too (v6.2 at time of writing).

Plugins are your only challenge here. If you use a lot of plugins, you will need to make sure they’re all updated to work with the latest WordPress version or they can cause your site to break. This is one of the biggest headaches with WordPress, but checking with the plugin author via their website should tell you if it has been updated or not.

In general, try to avoid plugins that haven’t been updated in the past 1 month as they may be abandoned (as a lot are) so won’t be patched up for new WordPress versions or to fix security issues.

If a plugin hasn’t had an update for 2 months replace it with one that has.

2. User accounts and Passwords

The password attack section was large because hackers have lots of ways to compromise your password, and fast computers to try lots of passwords very quickly. There are plugins you can install to help reduce this risk (mentioned below) but the first line of defence is not to use bad usernames and passwords in the first place!

User names: Don’t use the same user name as display name. The display name is visible on blog entries, comments and posts so if the user name is the same, you’ve told a hacker what a valid user name on your site is and made their job a lot easier! Make them different. Also, avoid just using the account owner’s first name, last name or anything obvious. If a hacker discovers that Bob Smith owns the site (which is easy to do from Facebook or LinkedIn), they will try that as a user name in various forms.

I suggest something random like: happy-coder-9272 for Bob Smith would be ok.

Admin accounts: Never EVER call your admin account “admin”. Just don’t. Or “Administrator”, or SiteAdmin or anything which easy easy to guess. Even with a strong password it will be the target of the hackers efforts as it clearly has full permission so a valuable trophy should they manage to compromise it.

Passwords: Make sure your passwords are long. The longer they are, the longer it takes for an automated attack system to guess it as it takes exponentially more time to guess all combinations of a password for every extra character you add.

Avoid single word passwords, (even long ones) as they’re easy to attack with a dictionary attack.

Try a password that combines words into a sentence that means something to you, even including a few non-letter characters can boost its security massively. E.g. WordPress-needs-updating-Regul4rly is a strong password. Most account systems also allow spaces in passwords which can help.

3. Turn off Comments unless needed

If you don’t need people to be able to comment on your posts, turn it off. It’s on by default usually and gives a hacker an easy entry point into your system to try and hack it. You’ll also receive a lot of spam from Hot Lady Who’s Lonely, or someone in a foreign country who’s a lawyer for an unknown extremely deathly recently deceased relative.

4. Require SSL

If you aren’t aware of SSL, it’s a simple system built into every web browser that secures the traffic to and from your website. It automatically encrypts all requests to your site, and encrypts what your site sends back to the browser which can be checked for authenticity. If you’ve ever seen a web address start with https:// then it’s the “s” that means “secure” and uses SSL.

It’s transparent to you and pretty strong, which means an attacker can’t easily sit in between your browser and the website, pretending to be the other party to try and compromise the site. This is called a man-in-the-middle attack and is common.

The easiest way to turn on SSL is to install a plugin. There are a number of them which will do this, but for reasons I’ll mention next, I recommend the one we use which is Really Simple SSL.

This is a free plugin that will force all requests to use SSL and once activated will ask you to allow it to do this. If a user tries to access your site using http:// (without SSL security) it will automatically redirect them to https to make sure they access the site securely.

The plugin also provides some excellent security features which you really should turn on, but I’ll talk about that soon.

Note about preparing your site for SSL: When using SSL to access the site, if you have links in your site to images, documents or maybe even other sites that use http:// rather than https:// then you’re introducing a potential vulnerability. For content hosted on the same site, you just need to go to each link and change it to https instead and it should work. If you have links to external sites or content that are http:// you may need to see if they support https first (just try putting the “s” in the URL in your browser and see what happens).

5. Monitor Your Site

This may sounds strange, but you need to know if your site is being attacked and how so you can see if the security changes you’re making are actually making a difference. It will also help you understand if they’re a new vulnerability you’re exposed to. Any good admin will tell you how important monitoring your site is, and it’s easy to do.

WordPress by default doesn’t have monitoring built in. I have no idea why as it seems so important, but there are great plugins to do this for you. The one we use is:
Limit Login Attempts Reloaded

Once you activate this you’ll see a new widget on your dashboard and a new item in your settings where you can see a log of all those login attempts, and what happened. You may be surprised to see that for every legitimate login, there are 100 attempts to log in from somewhere in China or a strange unexpected country. As a WordPress site, without proper security in place, this is normal but at least you can see it for yourself.

A great feature of Limit Login Attempts is that they allow you to choose automatic lock-out rules, which is definitely worth setting up. This basically says that after 2-3 failed attempts to log in, you can block that connection (IP Address) from being able to try again for as long as you wish. 60 minutes (we have ours set to 2 hours). After this, any further failed logins will block them for 24 hours which is a great way to hamper the hacker attempts to use automated guessing bots to guess passwords.

If one of your legitimate users do accidentally lock themselves out, the plugin also has an easy way to unlock them immediately and you can also whitelist certain IP addresses which stops the lockout from happening at all.

6. More Advanced (but easy to implement) Hardening Techniques

Rename wp-admin

All WordPress pages have a default login page called “wp-admin”which makes it easy for a hacker to guess where they can target for login attempts. A recommended defence against this is to install a plugin that will rename the wp-admin page to something else so more difficult to find.

Search the plugins list for “rename wp-admin” and you’ll find some good ones, but 2 suggestions are:

Just remember to take a note or bookmark the new login page as it’ll be easy to forget!

Turn off XML-RPC

There are 2 main ways to get into your site from the “front door” (i.e. using a username and password login) – wp-login and XML-RPC. Both are legitimate routes but for different purposes.

  • wp-login is basically the wp-admin page (hopefully you’re renamed this by now) and allows someone to log in via their web browser. For manual attacks this is an easy route, but can also be subject to automated attacks. You need to leave this route alone or you won’t be able to log in to administer your site, but renaming it from the default wp-admin URL will help hide it and reduce the risk of compromise.
  • xml-rpc is used when you have systems that need to integrate into your site to automatically administer it (e.g. monitor it, automate certain workflows by other systems etc). This doesn’t have a web page login like wp-login, and is used by other systems to log in “in the background”. As this can most easily be used by an automated attack system, this is the route they usually use for brute force attacks. Unless you need this (or have a system that integrates to your WordPress site) then you’re safe to disable xml-rpc and it will reduce your attacks by 90%. You can turn this off using the Really Simple SSL plugin.

Other Hardening

Hardening means to increase the security of your site and the above will get you most of the way to a site that is difficult to compromise. The Really Simple SSL plugin we recommended also includes some easy-to-activate hardening options we suggest you turn on.

These options are under Settings > SSL > Settings tab (at the top) and Hardening in the Settings panel of this page (see above screenshot).

I’ve described what each does below:

  • Disable the built-in file editors – this stops hackers who are on your site from editing your content if you have plugins that allow this. Usually not so best keep this turned off.
  • Prevent code execution in the public Uploads folder – if your site allows files to be uploaded by your site users, this will make sure the file can’t be something that can be “run” (such as a script or executable). There’s almost no reason to ever have this turned off.
  • Hide your WordPress version – your WordPress version is relatively easy to discover on your site. Hackers use this information to find sites that haven’t been updated or may be using WordPress versions with known vulnerabilities that can be exploited. The less you can allow a hacker to discover about your site, the less ammunition you’ll be giving them to help get into your site.
  • Prevent login feedback – this is an interesting one. Usually you want to tell a user that they’ve entered a bad password or mistyped their user name to help your legitimate users. However, this can be useful to an attacker who is trying to compromise your site. If a bad login tells them that it was either their username or password that was wrong, that tells the hacker that the username exists (or doesn’t) so they know whether to focus on keep trying different usernames or focus on cracking the password if the username is a real one.
    It’s a balance between helping your users or helping the hackers. You need to decide for yourself whether to turn this on or not.
  • Disable Directory Browsing – web servers can be setup to allow the contents of a folder on your website to be listed to the user. Most often this is turned off as it can exploited by hackers to add or modify files in your site.
  • Disable User Enumeration – user enumeration is where an attacker uses various techniques to scan your site for legitimate user accounts so they can try to attack known accounts. This is obviously a bad thing but not difficult to do on WordPress sites. Turning this option on (to disable user enumeration) is highly recommended.
  • Block the username “admin” – you should never use “admin” for a user account. Turning this on stops you accidentally doing that and will offer to rename any accounts called “admin” that you may already have.
  • Disable XML-RPC – as described above, this turns off the XML-RPC support on the site so it can’t be used to attempt logins using automated attack tools.
  • Block user registrations when login and display name as the same – this is to support good practice and makes it harder for attackers to find valid user accounts they can try to attack.

The Really Simple SSL plugin also includes other security options which will increase your site security even further but they’re include in the paid upgrade version so won’t be covered here.

By using the above free tools, techniques and plugins, you will strengthen your website against attacks significantly and will be more than enough for most WordPress sites.

But remember, your security is only as good as your users. Despite how often we train our teams to use strong passwords and follow good security practices, your security is only as good as the weakest link.
One final piece of advice is to make sure you spend as much effort monitoring your internal users as the external ones if you want to remain secure.

If your site becomes compromised

A final note on how to deal with the unfortunate (and hopefully unlikely) event that your site is compromised, and how to detect this.

  • Your site contains strange content
  • Attempts to log into a user account that you’re sure you have the correct password for, is failing.
  • Your site is seeing an abnormal amount of outgoing traffic
  • Your site is really slow for no reason
  • Your site is showing 404 regularly (but not always)

The above are signs that your site may have been compromised, and either being used as a “hub” for an attacker to send their spam or malware from, or maybe that they’re using for their own purposes.

1. If you’re able to log into the site (wp-admin)

Once you can log into the site and access it’s setup, you should immediately do the following:

  1. Change the passwords of all user accounts. Make sure to choose something long and complicated for each, and make sure no 2 passwords are the same.
  2. Check for pages, posts or plugins that you didn’t install. Remove them all immediately.
  3. Update all plugins to the latest versions. If the attacker got into your site through a vulnerability in a plugin, you need to make sure the vulnerability is plugged if possible.
  4. Update your WordPress version if not using the latest one. This may include your PHP version.
  5. If you aren’t using the recommended security plugins mentioned in this article, now is a great time to install and configure them!

If the site is a mess due to what the attacker has done, you may need to restore a previous backup of your site first (to before it was compromised) and then do all 5 steps above to secure it and avoid it being compromised again.

2. If you aren’t able to log in

If your WordPress site is hosted by someone such as GoDaddy or Ionos, your first and best option is to contact their technical support team and ask them for help. They have tools and access to your site so will be able to recover it and help you track down how the attacker got into your site.

If you host the site yourself, you will need to use more advanced techniques but if you have backups of your site, you may want to restore it first so you can log in again and follow the steps above.

I hope this helps – happy Blogging!

Supporting the Text Scale setting in Delphi (iOS and Android)

In Delphi (Firemonkey), when you drop a TLabel onto a form, it will have a default font size of 12. Have you ever noticed that changing the size to 11 or 13 doesn’t give the results you expect, and both are usually smaller than when set to 12. Strange!

Is it a bug? Well no, it actually isn’t.

This is because Delphi treats font size 12 as a special value which isn’t necessary 12 pts in size. It’s best to think of FontSize: 12 as “default” instead and will apply the font scaling setting from your phone.

The problem is that the font scaling only applies when your font size is 12, so your app won’t honour the phone text scaling option for headings, captions etc which have smaller font sizes applied. Ideally you’d want all test within your UI to uniformly scale up or down as needed.

Luckily, it’s easy to ask the operating system for the scaling and applying it yourself.

{$IFDEF IOS}
uses iOSapi.UIKit;
{$ENDIF IOS}
{$IFDEF ANDROID}
uses Androidapi.JNI.GraphicsContentViewText;
{$ENDIF ANDROID}

function FontScale: Single;
{$IFDEF ANDROID}
var
  Resources: JResources;

  Configuration: JConfiguration;
{$ENDIF ANDROID}
{$IFDEF IOS}
var
  f: UIFontDescriptor;

{$ENDIF IOS}
begin

  Result := 1.0;

  {$IFDEF ANDROID}

  if TAndroidHelper.Context <> nil then

  begin

    Resources := TAndroidHelper.Context.getResources;
    if Resources <> nil then

    begin
      Configuration := Resources.getConfiguration;
      if Configuration <> nil then
        Result := Configuration.fontScale;

    end;

  end;

  {$ENDIF ANDROID}
  {$IFDEF IOS}
  f := TUIFontDescriptor.OCClass.preferredFontDescriptorWithTextStyle(
          UIFontTextStyleBody);
  Result := f.pointSize / 17.0;

  {$ENDIF IOS}
end;

Note: When designing your app and deciding on the font sizes for labels, you should do this relative to a scaling of 1.0 so they’re consistent. Applying the FontScale() function value above will turn these “logical” font sizes into “actual” font sizes which respect the phone’s font scaling setting.

The function returns a scaling value you could directly multiply your logical font size with before applying to your control to get the desired effect. E.g.:

label1.Font.Size:=14 * FontScale;

The end result will be a UI which respects the text scaling setting on the device.

Tip: For ease I recommend creating a helper function which returns the actual font size from the logical one, such as:

function LogToActualFontSize(const logicalSize: Single): Single;
begin
  Result:=logicalSize * FontScale;
end;

How does this work?

Android is fairly easy – it returns the actual scaling factor to apply. E.g. the default is 1.0, smaller may be 0.8 and larger may be 1.5. We can apply these directly.

iOS is a little trickier as we can’t get a scaling directly (for good reason). Instead, we get what we need by asking the type system for the current size of the body text style.
This will already be scaled by the text size setting, so we can simply divide it with the default body style font size at “normal” test sizing (17.0) to get the ratio – i.e. scaling factor.

Caveats and Things to be wary of…

The above will give you an easy way to achieve a scaling app but doesn’t address a few issues listed below. Note that I have no solution for these but if anyone discovers one, please add a comment.

  • Font Sizes of 12 will still apply the scaling regardless of the above, so you will want to ignore those cases to avoid double-scaling. Personally, I use a font.size of 12.1 to avoid this but you could put a “if logicalSize = 12 then return 12;” line in the helper function.
  • If the user changes the font scaling setting on their phone while the app is open in the background, the app won’t get notified of the change. There are ways to do this through observers but it’s beyond the scope of this article. See this post for more details:
    https://stackoverflow.com/questions/18951332/how-to-detect-dynamic-font-size-changes-from-ios-settings
  • Test very carefully for all text scale values! Your labels are likely to be a fixed size in your UI but your font size won’t stay the same as when it was designed so may introduce cropping or overflow issues.
  • If your font text doesn’t change then the most likely culprit will be the StyledSettings property of the label/control. By default, all labels will use the default font size set by the style unless the font size is changed at design time. If you’re setting the size at runtime instead, the new size will be ignored. Uncheck the Size option in this property to avoid Firemonkey ignoring the font size you set in code, or do the following in your code:

    label.StyledSettings:=label.StyledSettings – [TStyledSetting.Size];

I hope this helps you achieve a better behaving and more accessible user experience in your Delphi app.

For reference, the following articles were really useful in discovering how to do what’s described in this article:

https://programmersought.com/article/15291678656/

https://gist.github.com/zacwest/916d31da5d03405809c4

Welcome to FMX (Firemonkey) Blog

I’ve been developing with Delphi for 20 years since Delphi 1 and it’s been an interesting ride, with lots learned each year. I took a break during the “dark years” of Delphi 8 through XE but since then have been using every version of Delphi.

Delphi may not be as popular a language as it used to be, mainly due to fashion changes, the trendy new developers moving away from all-in-one IDEs, and preferring Open Source, web-originating frameworks and languages. However, it’s still a powerful, modern language capable of incredible things, and well worth looking into if you’re new to the coding arena and want to try something that is genuinely cross-platform.

The Firemonkey framework has been the most interesting additions to the Delphi family, adding support for iOS, Android and MacOS, both 32bit and 64bit variants – and now even Linux Desktop and server support. It’s a bold move on Embarcadero’s part but one that will hopefully pay off for them.

Our mainstream product, MyShiftPlanner, is one of the most popular shift worker time management app on the stores and is written completely in Delphi using Firemonkey. It’s taken 5 years to get it where it is and was initially written in XE5 but now runs like a dream under Delphi Rio 10.3.3.

I’ve learned a lot along the way which I wanted to share with anyone else who wants to take advantage of the power of this truly cross-platform framework.

Each article covers a specific topic but mostly helping to extend what Firemonkey does out-of-the-box so you can take advantage of even more for your apps. The blogs will cover how to add in-app subscriptions to your apps, how to include third party frameworks, use more native controls than are supplied by Firemonkey and more. I’ll also include links to other people’s blogs which offer similar benefits so you can find what you need in one place.

I hope you enjoy this blog and find it useful. Please continue to share/ promote the benefits of Delphi and support your fellow Delphi community developers.

Happy coding!